I have found that, the case 1 and case 3 can be compiled without errors, but case 2. (SubClassB extends SuperClassA, which is abstract class) What I wonder is, why case 1 and case 3 have no compile errors. If it is a JDK bug, why case 2 can't pass the cast check?
// case 1
List<SuperClassA> a = new ArrayList<>();
SubClassB b = (SubClassB) a;
// case 2
List<Number> m = new ArrayList<>();
Long n = (Long) m; //Error:(xx,yy) java: incompatible types: java.util.List<java.lang.Number> cannot be converted to java.lang.Long
// case 3
List<Exception> e = new ArrayList<>();
RuntimeException d = (RuntimeException) e;
In the case of unsafe casts, you get a warning as the compiler can't work out whether this cast should be allowed or not. (It was allowed without warning in older Java versions so hard to remove now)
Note: Long
is a final
class and it assumes there is no way there is a sub-class of Long
where this cast could work.
You can create a class like
class MyException extends RuntimeException implements List<Exception> {
so these are valid lines of code
List<Exception> e = new MyException();
RuntimeException d = (RuntimeException) e; // ok.
Note that case 1 and case 3 will both fail at runtime.
At compile time, the compiler will complain about incompatible casts only if the language spec says so. The language spec allows cases 1 and 3 because they will not 100% fail.
Case 1 will not 100% fail , as far as the compiler is concerned, because it thinks that a
can contain an instance of any type that implements List<SuperClassA>
. What if there is a subclass of SubclassB
that actually implements List<SuperClassA>
? And what if a
actually contains an instance of that subclass of SubclassB
? Then the cast would succeed!
Same thing goes for case 2. What if there is a subclass of RuntimeException
that actually implements List<Exception>
? And what if e
actually contains an instance of that subclass of RuntimeException
? Then the cast would succeed!
Case 2 shows an error because Long
is final
. There couldn't be a subclass that implements List<Number>
, so it will definitely fail.
This is specified in §5.5.1 of the spec (italics are the relevant bits):
Given a compile-time reference type S (source) and a compile-time reference type T (target), a casting conversion exists from S to T if no compile-time errors occur due to the following rules.
...
If S is an interface type:
If T is an array type, then S must be the type
java.io.Serializable
orCloneable
(the only interfaces implemented by arrays), or a compile-time error occurs.If T is a class or interface type that is not
final
(§8.1.1), then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might).
If T is a class type that is final, then:
– If S is not a parameterized type or a raw type, then T must implement S, or a compile-time error occurs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With