This: (SomeType) someExpr
is the casting operator. It's a bit of a tricky beast, as the cast operator is used for 3 completely different operations. Guns and Grandmas over here. If SomeType
is primitive, it converts things. If SomeType
is a plain jane type, something like String x = (String) obj
, it is a type assertion: It doesn't just make the compiler adjust the type of the 'AST node', it also injects some bytecode that will do nothing if obj
is indeed a String
, but will cause a ClassCastException to be thrown if it is not.
Then there is the 'generics type coercion', the third thing casts do. It's what you are doing in these examples - g
is already of type Box
, so the only part you're actually adding is the generics bit.
This does nothing at runtime. No code is injected. You're telling the compiler: "Trust me, I know this is true, I understand there will be absolutely no checking of any sort". If you mess up and it's not true, some code that contains absolutely no casts whatsoever will nevertheless end up throwing a ClassCastException
:
List<String> list = new ArrayList<String>();
list.add("Hello");
List<Integer> hmm = (List<Integer>) (List) list;
System.out.println(list.get(0));
The 4th line will throw a ClassCastEx - even though the 'cast' appears to be on the 3rd line.
These 'generics type coercion' things have only one purpose: To let you work with legacy code; legacy code where the type safety of generics cannot be applied, simply because the code doesn't have any generics. It's been, what, 25 years since generics were introduced (with java 1.5), but the language had to cope with the fact that java 1.1 through java 1.4 were quite popular (in fact, java was arguably the most popular language on the planet at that point in time), so the generics feature is designed to cope with 'legacy' (read: lacks generics info) code.
If you find that you need generics type coercion to make your code work, take a step back, because you really shouldn't be doing that, except for extremely simplistic cases, and almost always in the form of casting straight to a type var (e.g. return (T) arr[i];
, which is in the get(int idx)
impl of ArrayList
, for example). This doesn't do anything either (it would be impossible for that line to produce a ClassCastException; no code is injected at runtime, it's purely a figment of the compiler's imagination), but it lets you e.g. use an array to represent a generics-based data structure, exactly what ArrayList is trying to do.
In other words, yes, you are right, generics is supposed to be invariant so it feels weird that you can cast one type to another, seemingly entirely incompatible type - but that's sort of the point: To let you work around missing / miswritten generics in libraries.
So, why does the first snippet not work? Because you're telling the compiler: Please treat this expression of type Box<?>
as type Box<T>
. If T isn't defined here, this doesn't work at all (compiler would have no idea what T is). If it IS defined, then this still does not work - T is not an Object. This is just basic java generics invariance rules: You have an expression that you told the compiler: Just treat it as a Box<T>
and don't complain, please - and are trying to assign that to a Box<Object>
which is not a valid assignment.
In the second snippet, you're telling the compiler: I know, I know, that g
is Box and not compatible, but will you shut up and just take my word for it, that you can treat it as a Box<Integer>
and just carry on? So, the compiler does, and sees an assignment of a thing that you told the compiler is a Box<Integer>
to a variable of type Box<Integer>
which is fine, so java will let you do this, though it will emit a warning to whine about the fact that you think you know better than the compiler does. It doesn't like it when you do that.