I have seen this question, but it does not seem to answer both cases below and does not reference any further documentation, which I'd love to read.
I have the following fragment of code:
public static void main(String[] args) {
// compile-time OK
// run-time ClassCastException
Child noWarnings = (Child) getParent();
// vs...
// compile-time failure
Child compileTimeFailure = new Parent();
}
private static Parent getParent() {
return new Parent();
}
private static class Parent { }
private static class Child extends Parent { }
The code on line 4 of the snippet produces no warnings, but results in a ClassCastException
. Why is the compiler unable to typecheck this?
This gets even weirder if we add a layer of indirection:
public static void main(String[] args) {
// compile-time OK
// run-time ClassCastException
Boxed noWarnings = new Boxed((Child) getParent());
// vs...
// compile-time OK, but warning is emitted correctly
// run-time ClassCastException
Boxed classCastWarning = new Boxed((Child) new Parent());
}
private static Parent getParent() {
return new Parent();
}
private static class Boxed {
public Boxed(Child data) {
}
}
private static class Parent { }
private static class Child extends Parent { }
It seems un-intuitive that the compiler does not allow the first example, but allows the second.
Any light shed on either of these case would be most welcome.
CodePudding user response:
Why is the compiler unable to typecheck this?
Some Parent
instances are instance of Child
, because all Child
s are Parent
s. The compiler doesn't know if the instance that will be returned is a Child
or not; but it could be. So it allows it.
In fact, as Stephen C points out, JLS does not allow the compiler to call this a compilation error.
Specifically, the language spec says:
If the compile-time type of the operand cannot be converted by casting conversion (§5.5) to the target type specified by the cast operator, then a compile-time error occurs.
(It also describes another situation in which compile-time errors occur in casting expressions; but these don't apply here because there isn't one or more AdditionalBound
terms).
Parent
can be converted to Child
by a narrowing reference conversion, so a compile-time error doesn't occur.
Boxed classCastWarning = new Boxed((Child) new Parent());
This is obviously going to fail. new Parent()
isn't an instance of Child
, it's an instance of Parent
.
But the compiler doesn't actually consider anything about new Parent()
in the context of the cast beyond the fact it's an expression of type Parent
, which it has to allow you to cast to a Child
.
But that's not to say that tooling such as an IDE isn't allowed to give you a warning (or even an error, if you have it so configured): it's clear via static analysis that new Parent()
is going to be an instance of Parent
exactly (because that's all new Parent()
can be), so casting that to a subclass is going to fail. Your tooling can tell you things that aren't specifically covered in the spec.
CodePudding user response:
Apart from existing answer, I would like to stress the reason why it's not easy to compute this exception during compile time.
The below example is also from the excerpt from https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.5 with minor additions from me.
class Point { int x, y; }
interface Colorable { void setColor(int color); }
class ColoredPoint extends Point implements Colorable {
int color;
public void setColor(int color) { this.color = color; }
}
final class EndPoint extends Point {}
class Test {
public static void main(String[] args) {
Point p = new Point();
ColoredPoint cp = new ColoredPoint();
Colorable c;
// The following may cause errors at run time because
// we cannot be sure they will succeed; this possibility
// is suggested by the casts:
cp = (ColoredPoint)p; // p might not reference an
// object which is a ColoredPoint
// or a subclass of ColoredPoint
// this wont throw casts exception as references to object is valid
Point refPoint = new ColoredPoint();
cp = (ColoredPoint)refPoint; // perfectly valid
c = (Colorable)p; // p might not be Colorable
// The following are incorrect at compile time because
// they can never succeed as explained in the text:
Long l = (Long)p; // compile-time error #1
EndPoint e = new EndPoint();
c = (Colorable)e; // compile-time error #2
}
}
This example is self-explanatory on its own most cases, but you can give further read to the documentation above.
CodePudding user response:
ClassCastExceptions
never occur at compile time. All exceptions occur at runtime. You can check the settings of your IDE to get hints while coding.