I get a ClassCastException when eclipse suggested that my code should be like that..
I have a class named K开发者_如何学编程ort.
ArrayList<Kort> kort = new ArrayList<Kort>();
then I use toArray()
, and eclipse suggest it should look like: Kort[] array = (Kort[])kort.toArray()
;
But it gives me this exception: ClassCastException ! :(
My suggestion is:
kort.toArray(new Kort[0])
Technically this might be fractionally slower than giving the correct size. However, you don't need to mention the collection variable name twice, so less chance of making a mistake. It's also easier to read. As a bonus, it also works with concurrent collections, where calling size
doesn't really make a great deal of sense.
However, the best approach is to avoid using arrays of references if you possibly can.
What you can do is:
Kort[] array = kort.toArray(new Kort[kort.size()]);
That is considered better, as it keeps the type safety on the array. If you don't care/want that, then just ignore the suggestion. The toArray() method without parameters returns an Object[] array.
Use
kort.toArray(new Kort[kort.size()]);
The root of the problem is the difference between the Object[] toArray()
and Object[] toArray(Object[])
methods in the java.util.Collection
API.
The first form allocates an array of the right size to hold the members of the collection and then assigns the member references into the array. Because the collection class doesn't know what the type of the members actually is (see note below), the API specifies that the result is an instance of Object[]
; i.e. as if it was allocated using new Object[size]
.
The second form takes an array as a parameter that will (normally) be where the collection member references are stored. Thus, the program determines what the type of the result will be by passing an array of the required type. If the supplied array is big enough to hold the collection elements, the toArray
method will return the supplied array instance. If not, a new array is allocated with the same type as the supplied array instance. (This can done reflectively using Array.newInstance(clazz, size)
where clazz
is the supplied array's component class. Note that the type parameter is not used to do this ... and it cannot be used.)
So what is happening is that Eclipse is not smart enough to know that the real correction to your mistake is to use a different method overload. In order to make this correction, it would need to "know" about the specific semantics of the two forms of the toArray
method. That's a tall order IMO.
The lesson is that Eclipse's suggested corrections are Just Suggestions ... and not guaranteed to be the right fix.
Note: The collection class does not know the appropriate array class to create in the toArray()
case because of type erasure. Information about how the type was instantiated is not available to the class that implements toArray
. But considering that it is generally possible to insert into a Collection<T>
something that is not a T
(by ignoring "unsafe typing" compiler warnings) it is maybe a good thing that the result type is an Object[]
!
This problem predates generic types; the two forms of the method have been present since the introduction of the collections framework in Java 1.2.
EDIT: in a comment @Thilo suggested that this problem would not have arisen if Java had supported generics from the start. My answer to that is that it didn't happen that way, and we cannot say what would have happened if they had. But we can say that with Java generics as they are currently defined it is would be impossible to do this without redesigning the collection APIs.
Specifically, a generic class cannot figure out what actual type has been used as a type parameter for a given instance. Without this information, it cannot know the correct array type to instantiate. In practice, if you want a generic type to create instances of the type parameter or some related type, you have to design the APIs so that the relevant methods have runtime access to the actual parameter type's Class
object. For example, the actual Class object could be supplied as a constructor parameter.
Another advantage of
kort.toArray(new Kort[kort.size()]);
over
(Kort[])kort.toArray();
is that in case your kort collection is empty, the resulting to array shall be a zero length array of object type which will throw an exception if typecasted to Kort.
Using kort.toArray(new Kort[kort.size()]);
ensures that even if the collection is empty still the returned array is of desired type.
精彩评论