If I try to cast a String
to a java.util.Date
, the Java compiler catches the error. So why doesn't the compiler flag the following as an error?
List<String> strList = new ArrayList<>();
Date d = (Date) strList;
Of course, the JVM throws a ClassCastException
at runtime, but the compiler doesn't flag it.
The behavior is the same with javac 1.8.0_212 and 11.0.2.
The cast could technically be possible. It can not easily be proven by javac that it is not in your case and the JLS actually defines this as valid Java program, so throwing an error is not allowed.
This is because List
is an interface. So you could have a subclass of a Date
that actually implements List
disguised as List
here. And then casting it to Date
would be correct. For example:
public class SneakyListDate extends Date implements List<Foo> {
...
}
And then
List<Foo> list = new SneakyListDate();
Date date = (Date) list; // This one is valid, compiles and runs just fine
In order to detect that this is not happening in your specifc case, the compiler would need to invest much more effort in that check, possibly even requiring runtime information. So the compiler only prevents casts that are absulutely impossible because the class-tree has no possibility to match at all. Which is not the case here, as seen.
On top of that, the JLS requires your code to be a valid Java program. In 5.1.6.1. Allowed Narrowing Reference Conversion it says:
A narrowing reference conversion exists from reference type
S
to reference typeT
if all of the following are true:
- [...]
- One of the following cases applies:
- [...]
S
is an interface type,T
is a class type, andT
does not name afinal
class.
So even if the compiler could figure out that your case is actually provable to be impossible, it is not allowed to throw an error because the JLS defines it as valid Java program.