Sunday, 29 July 2012

Java: instanceof, isAssignableFrom() or isInstance()?

The instanceof, Class.isInstance()and Class.isAssignableFrom() statements all aim at testing whether an object can be casted without triggering an exception.
public class Test {

    public static class A {};
    public static class B extends A {};
    public interface I {};

    public static void main(String[] args) {

        A a = new A();
        B b = new B();

        //
        // instanceof
        //

        if ( B instanceof A ) {} // Illegal
        if ( B instanceof a ) {} // Illegal
        if ( b instanceof a ) {} // Illegal
        if ( b instanceof A ) {} // OK
        if ( b instanceof I ) {} // OK

        if ( null instance A ) {} // OK
        if ( b instance null ) {} // Illegal

        if ( b instanceof b.getClass() ) {} // Illegal

        if ( b instanceof double ) {} // Illegal
        if ( b instanceof Double ) {} // Illegal
        if ( b instanceof double.class ) {} // Illegal
        if ( b instanceof Double.class ) {} // Illegal

        //
        // isAssignableFrom()
        //

        // All OK 
        if ( A.class.isAssignableFrom(B.class) ) {}
        if ( A.class.isAssignableFrom(b.getClass()) ) {}
        if ( a.getClass().isAssignableFrom(B.class) ) {}
        if ( a.getClass().isAssignableFrom(b.getClass()) ) {}

        // All OK
        if ( double.class.isAssignableFrom(double.class) ) {}
        if ( Double.class.isAssignableFrom(Double.class) ) {}
        if ( double.class.isAssignableFrom(B.class) ) {}
        if ( Double.class.isAssignableFrom(b.getClass()) ) {}

        // All OK
        if ( I.class.isAssignableFrom(double.class) ) {}
        if ( I.class.isAssignableFrom(Double.class) ) {}

        Class c = null;

        // Throws NullPointerException at runtime
        if ( A.class.isAssignableFrom(c) ) {}

        //
        // isInstance()
        //

        b = null;

        // All OK
        if ( A.class.isInstance(b) ) {}
        if ( a.getClass().isInstance(b) ) {}
        if ( double.class.isInstance(b) ) {}
        if ( Double.class.isInstance(b) ) {}
        if ( I.class.isInstance(b) ) {}

         // Throws NullPointerException at runtime
         if ( c.isInstance(a) ) {}

    }

}

About instanceof:
  • The left argument must be a reference to an object
  • The right argument must be a class known at compile time
  • The left argument cannot be a primitive
About isAssignableFrom():
  • The left argument must be a class (not necessarily known at compile time)
  • The right argument must be a class (not necessarily known at compile time)
  • The left argument can be a primitive
About isInstance():
  • The left argument must be a class (not necessarily known at compile time)
  • The right argument must be a reference to an object (eventually null
  • The left argument cannot be null

Conclusion

  • The isAssignableFrom() and isInstance() methods are more flexible than instanceof in that they do not require to know the right argument at compile time.
  • The left argument of instanceof can be null, contrary to the isAssignableFrom() and isInstance() methods.
  • The instanceof statement cannot be used with primitives.
  • Only isInstance() can take a null right parameter without a generating compile time issue or throwing a NullPointerException at runtime.
  • Only instanceof and isAssignableFrom() can be used to directly test interface implementation.
Therefore, the most stress-free solution to check whether an object (null or not) can be cast without triggering an exception at runtime is isInstance(). However, it cannot be used to check whether a Class implements an interface. In this case, isAssignableFrom() remains the best solution.