Consider the following clause of JLS 8
- Here the
interface G<>
is the generic super type that is being used to generate other types based on the clause: - Not sure why I am getting compilation error at that section?
- In the
S -> T
conversion - my target type is Parameterized type and final type which is complying all the rules that JLS mentions for validity of the cast operation.
CodePudding user response:
You've overcomplicated matters. Let's first ditch the generics:
class Scratch {
public static void main(String[] args) {
S_intrf S = new S_clss();
T_clss T = (T_clss) S;
}
}
interface G {}
interface S_intrf extends G {}
class S_clss implements S_intrf {}
interface X extends G {}
final class T_clss implements X {}
and try to compile that, which produces the exact same error. Hence the generics don't matter and just muddy the waters. They do not explain this error.
As you can see from your own diagram you made, there is an exclusionary principle at work:
- There is some unknown type; this is the actual type of the object that expression
S
resolves to. - We do know a few things about this unknown type; we know it has to be an object (because it's a reference variable), and that object has
S_intrf
in its type hierarchy. After all, if that wasn't the case, we could never get here; the JVM does not let you assign a reference to an object that isn't to variableS
, given that it is declared as being of typeS_intrf
. This is an absolute guarantee: You can't makejavac
compile code so that this doesn't hold, and if you try to use bytecode manipulation shenanigans, the verifier will refuse to load the class.
Given that knowledge, we can surmise one more thing: This unknown type, whatever it might be, cannot possibly be T_clss
or any subtype of it. We know this because T_clss
is final (therefore, no subtype of it can exist), and, it does not itself implement S_intrf
.
Hence, the cast will necessarily fail, it can't not. And in the Java Lang Spec, there's a rule that says the compiler must reject such a situation with a compile time error.
We can 'prove' this by reading the JLS, but I'll leave that as an exercise to the reader. A more pragmatic way to show this truly is the answer, is to simply remove the final
. Now, it does compile! If you then run it, you get, as expected, a ClassCastException
.
Without final
, one could write e.g. class Example extends T_clss implements S_intrf {}
and now we have a type such that an instance of this could both [A] be referred to by variable S
, and [B] can be cast to T_clss
.
By the rules of JLS, the fact that a code analyser can trivially figure out that variable S
in fact refers to an instance of S_clss()
is not taken into consideration. The rules of the analysis that leads to 'oh, the analyser says this code will always fail, therefore refuse to compile it' is set in stone, spelled out by the JLS, and a compiler must adhere to it exactly.
The §5.5.1 clause is most likely referring to the fact that you can use the cast operation to make assertions about the stuff in the generics part; you can for example do:
List<Object> foo = ...;
List<String> bar = (List<String>) foo;
This compiles, with a warning, as the java compiler is not going to generate any code or give you any guarantees that the list in fact only contains strings. §5.5.1 is talking about casts where the non-generics part is useless (e.g. casting an expression of type List
to List
, which is obviously pointless), but the generics part is relevant - e.g. the above snippet.