Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a List<SuperClass> Object can be casted to a Subclass Object?

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;
like image 408
Ophiuchus Hahn Avatar asked Jan 01 '23 03:01

Ophiuchus Hahn


2 Answers

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.
like image 148
Peter Lawrey Avatar answered Jan 03 '23 17:01

Peter Lawrey


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 or Cloneable (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.

like image 26
Sweeper Avatar answered Jan 03 '23 15:01

Sweeper