5

The following code compiles fine:

interface Flyer{ }
class Bat { }

public class App {

    public static void main(String[] args) { 
        Bat b = new Bat();
        if(b instanceof Flyer) System.out.println("b is a Bird");
    }

} 

If we make the Bat class final, the code doesn't compile:

final class Bat { } 

If the final class implements Flyer, it compiles fine:

final class Bat implements Flyer { } 

Anyone care to explain the logic behind this?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
html_programmer
  • 18,126
  • 18
  • 85
  • 158

3 Answers3

6

When you make class Bat final, you're saying that this class can't be sub-classed. Since Bat does not implement the interface Flyer, the compiler is able to determine that b instanceof Flyer can never be true and raises an error.

This is specified in JLS section 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.

Additionally, from section 15.16 about cast expressions:

It is a compile-time error if the compile-time type of the operand may never be cast to the type specified by the cast operator according to the rules of casting conversion (§5.5).

In this case, Bat can never be cast to Flyer: it doesn't implement it and final ensures that there can't be sub-classes that would implement it.


As you found out, the fixes are:

  • Make Bat implement Flyer: in this case, the instanceof operator would always return true.
  • Remove the final identifier, implying that there could be sub-classes of Bat implementing Flyer.
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • Wouldn't `null instanceof X` return true regardless of the "type" of the null instance ? However I can see that this would be very dodgy to let the compiler accept that – Dici Jan 03 '16 at 15:30
  • @Dici Actually, [`null instanceof X` is always `false`](http://stackoverflow.com/questions/2950319/is-null-check-needed-before-calling-instanceof). – Tunaki Jan 03 '16 at 15:32
  • Ups... sorry, you're right, I even use this for writing all my `equals` methods. I need a sleep :D – Dici Jan 03 '16 at 15:41
4

Well, if Bat is a final class and it doesn't implement Flyer, it can't have any sub-class that would implement Flyer either, so instanceof can never return true. In such cases the compiler doesn't allow this expression (i.e. (x instanceof Y) is only allowed if there's a chance that x would contain a reference to an instance that implements or extends Y).

In your second snippet Bat already implements Flyer, so b instanceof Flyer would always return true, regardless of whether Bat is final or not.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • I find it weird (maybe a bad design) that if it's always `false` a compilation error will happen, but if it's *always* `true`, then it's fine. – Maroun Jan 03 '16 at 15:28
  • Seems logic now... early checking always a good idea. – html_programmer Jan 03 '16 at 15:30
  • @MarounMaroun There is some inconsistency here, I agree. – Eran Jan 03 '16 at 15:32
  • 1
    @MarounMaroun there's the same for `if (true)` and `if (false)`. The first compiles fine, the second raises a warning. I think the rational is that dead code is worse than useless checks. If you write some conditional code which will always be executed, well fine, life is just easier than you thought, but if you write some code that will never be reached, you may make wrong assumptions about the behaviour of your program that could cause harm – Dici Jan 03 '16 at 15:35
4

In addition to @Tunaki's reference from the JLS, this is also explicitly explained in https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1, quoting relevant rule:

If S is a final class (§8.1.1), then S must implement T, or a compile-time error occurs.

An instanceof check follows these rules for reference type casts.

M A
  • 71,713
  • 13
  • 134
  • 174