Reputation: 619
I have an ArrayList<Number> which I read out of a file. But I need it to be an ArrayList<Integer>. I know all the Numbers are Integers, so I thought I could do something like this:
ArrayList<Number> numbers = new ArrayList<>();
nubers.add( 1 );
// cast the list<Number> to a list<Integer>
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
ints.add( 2 );
However, this causes an compile error. But if I first cast numbers to an Object, the code runs perfectly (except of-course for the unsafe cast warning).
ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;
So why does the compiler complain at my cast when it's a valid cast?
Note: I'm not asking for a solution but for an explanation of why the cast does not compile.
Bonus question:
Thanks for your explanations, but they make me wonder why this line can compile in some environments? A brief summary of the line:
A Collection<Bundleable> is being cast to Collection<? extends Item> where Item implements Bundleable.
Upvotes: 2
Views: 520
Reputation: 36391
Remember that generics are not real types, there is type erasure for them. So the compiler tries to verify many things about the way you use generics before compiling to bytecode. As ArrayList<Integer>
is not as subtype of ArrayList<Number>
(even if: Integer
is subtype of Number
or your list of Number
s is full of Integer
s), the compiler complains about the direct cast as it may be too dangerous (not really related types).
In the second case, you pass over the compiler type-checking in some way, because of course every of your types is subtype of Object
and the compiler can't enforce a rule, but he can warns you about the downcast...
Upvotes: 0
Reputation: 521028
Consider the following declaration:
ArrayList<Number> numbers;
the ArrayList
numbers
could later be instantiated with any generic which extends the Number
class, such as:
numbers = new ArrayList<Float>();
Now, you can see the problem with the following cast:
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
But numbers
contains Float
s, not Integer
s. By the way, making the cast to Object
does compile, but does so with a warning:
ArrayList<Integer> ints = (ArrayList<Integer>)(Object)numbers;
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
The compiler is complaining about an unchecked cast, because it correctly deduces that the generic types may not match.
Upvotes: 1
Reputation: 213223
The type information of list is not available at runtime, i.e, generics are not reifiable. So at runtime, the cast will anyways work, as it's just like this:
ArrayList ints = (ArrayList)numbers;
No worries. But if allowed, the cast will result in issues down the line, like in below modified code of yours:
ArrayList<Number> numbers = new ArrayList<>();
nubers.add( 1.0 );
// cast the list<Number> to a list<Integer>
ArrayList<Integer> ints = (ArrayList<Integer>)numbers;
Integer x = ints.get(0); // trouble here
Basically you added a double
type to a List<Number>
which is perfectly fine. If compiler allowed the casting, then it will also pass at runtime.
Going further, the last line (commented one) will fail with ClassCastException
, because you're just trying to assign a Double
type to Integer
type. That's the reason compiler doesn't allow to do such casts. And you should not ignore unchecked cast warnings by compiler in case of generics (even though that is warning).
Upvotes: 4