instanceof operator - why there is Illegal compile

2020-03-04 08:51发布

问题:

Considering the following code, I don't understand why "System.out.println( c2 instanceof D);" will result an "illegal compile time error" but not return "false"? Many thanks for your help!

interface I { }
class A { int x = 1;}
class B extends A implements I { int y = 2;}
class C extends B { }
class D extends B{ }
class E implements I { }
C c2 = new C();`

回答1:

The error from Java 8 is:

error: incompatible types: C cannot be converted to D

And indeed, C and D are not in the same lineage (other than both being Object). Since the compiler can tell you at compilation time that the instanceof will never be true, it does. The earlier a problem is caught, the better; the compiler is preventing your having code that is unnecessary or a condition that will never be satisfied. It's like the error you get when you have code that can never be reached because the logic is unambiguous and never allows execution of the code (error: unreachable statement).

Here's a complete example:

public class Example {

    interface I { }
    static class A { int x = 1;}
    static class B extends A implements I { int y = 2;}
    static class C extends B { }
    static class D extends B{ }
    static class E implements I { }

    public static final void main(String[] args) {
        C c2 = new C();
        System.out.println(c2 instanceof D);
    }
}

Which fails with:

Example.java:12: error: incompatible types: C cannot be converted to D
        System.out.println(c2 instanceof D);

But, if you make it so the compiler can't know for sure that the instanceof will always be false, then it does indeed compile and you get false at runtime:

public class Example {

    interface I { }
    static class A { int x = 1;}
    static class B extends A implements I { int y = 2;}
    static class C extends B { }
    static class D extends B{ }
    static class E implements I { }

    public static final void main(String[] args) {
        C c2 = new C();
        doTheCheck(c2);
    }

    static void doTheCheck(Object o) {
        System.out.println(o instanceof D);
    }
}

Since what we're checking, o, could be anything, the compiler doesn't alert you to the invariant check, the code compiles, and you get false as output.



回答2:

Because compiler knows that casting c2 to type D will always doing to fail at runtime, and hence is marked as compile time error. So it doesn't allow such instanceof to pass through.

Quoting JLS §15.20.2:

If a cast (§15.16) of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.



回答3:

Thats because compiler can check at compile time that such instanceof would always return false, you can read on it in JLS:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2

If a cast of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.

RelationalExpression is first operand, and ReferenceType is second: RelationalExpression instanceof ReferenceType