instanceof (Pattern Matching) JEP 305

imagarg

Ashish Garg

Posted on February 26, 2020

instanceof (Pattern Matching) JEP 305

Every java developer have written code like below:

if(str instanceof String ){
    int len = ((String)str).length();
}

In above snippet

  • First a test is happening to confirm ‘str’ is of type String class, if yes
  • ‘str’ is getting cast into a String object, after successful casting
  • length is getting calculated and assigned to a int.

You must have asked this question why I have to do this casting if I already checked the type before. After successful type check only control will transfer inside if block then ‘str’ can only be String or its subclass there is no other possibility compiler should not asked me to this unnecessary casting.

And there is always a possibility of writing buggy code like this:

if(str instanceof String ){
    int len = ((Integer)str);
}

this will compile just fine by your IDE, and you will be seeing a nice “java.lang.ClassCastException” at runtime.

JEP 305 (Preview in JDK 14)

The instanceof has been extended to support type test pattern. Now you will be coding like this:

if(str instanceof String s){
    System.out.println("length is "+s.length());
}

here instanceof is checking type of ‘str’ as String and assigned it to a binding variable ‘s’. This looks much neat in comparison to older version.

Few important things to keep in mind:

  • Scope of binding variable

One thing to keep in mind that scope of binding variable is only lies in true statement i.e. in below code block

  if(str instanceof String s){
  }else{
      System.out.println(s.length());
  }

trying to access binding variable ‘s’ in else block will be compile time error “cannot find symbol” as 's' is not visible in else block.

Similarly if you have written a code snippet like below

  private static void newInstanceOfNeg(Object str){
      if(!(str instanceof String s)){
      }else{
          System.out.println(s.length());
      }
  }

‘s’ will be accessible in else block only any attempt to access it in if block will be compile time error.

  • If name of binding variable matched with another variable in scope of method or class, it can be nasty place for bug to hide. So we need to be very careful about naming them

    • Binding variable name in method scope

    Observe below snippet, here binding variable named as ‘s’ and method also has another local variable with same name

    private static void instanceOfFunWithACaveat(Object obj){
        String s = "Ash";
        if(obj instanceof String s){ 
            System.out.println("length is "+s.length());
        }
    }
    

    good part is, this will not compile and you will be seeing a error like “error: variable s is already defined”.

    But if you write something like

    private static void instanceOfFunWithACaveat(Object obj){
        String s = "Ash";
        if(obj instanceof String str){ 
            System.out.println("length is "+s.length());
        }
    }
    

    here binding variable named as ‘str’ but under true block code is still referring to method variable ‘s’. It will compile and run perfectly without any error but such kind of typo might turned out to be a bug.

    • If binding variable has same name as instance variable

    Q- What will happen if binding variable has same name as instance variable and you try to access it in true block, see below snippet ?

    private static String s = "Ash";
    private static void instanceOfFunWithoutElseAndACaveat(Object obj){
        if(obj instanceof String s){
            System.out.println("length is "+s.length());
        }
    }   
    

    and if you are calling this function with

    instanceOfFunWithoutElseAndACaveat("Ashish")
    

    you will be seeing output as ‘6’ not ‘3’. Binding variable will take precedence here.

    Q- What will happen if binding variable has same name as instance variable and you try to access it in false block, see below snippet ?

    private static String s = "Ash";
    private static void instanceOfFunWithElseAndACaveat(Object obj){
        if(obj instanceof String s){
            System.out.println("length is "+s.length());
        }else{
            System.out.println("s is instance variable here"+ s.length());
        }
    }
    

    and if you are calling this function with

    instanceOfFunWithElseAndACaveat(1); 
    

    you will be seeing output as ‘3’ because in else block instance variable ‘s’ is visible.

    • Test pattern with && operator

    Usually conditional if block has multiple operator to fulfill certain business need. Let’s take a example where ‘&&’ operator is present along with instanceof test pattern, see below snippet:

    private static void instanceOfWithAndOperator(Object obj){
        if(obj instanceof String s && s.contains("Ash")){
            System.out.println("In true block");
        }
    }
    

    here you can see, in if block after instanceof test pattern there is also a contains check separated by ‘&&’ operator. Right side of ‘&&’ will only be execute if instanceof test pattern succeeded.

    • Test pattern with || operator

    Similar to above, let’s say we have || operator after instanceof test pattern

    private static void instanceOfWithOrOperator(Object obj){
        if(obj instanceof String s || s.contains("Ash")){
            System.out.println("In true block");
        }
    }
    

    ‘s’ will not be accessible to right side of ‘||’ block neither in if statement nor in else block (if there is instance variable with same name ‘s’ that will be accessible )

    • Test pattern with || operator

    Let’s say if you are calling below method with null argument

    private static void newInstanceOf(String str){
        if(str instanceof String s){
            System.out.println("In true block");
        }else{
            System.out.println("In false block");
        }
    }
    

    it will simply print “In false block”, instanceof test pattern will only be executed for non null object.

Compile and Run

As record released as preview feature with JDK 14, you need to enable preview to play around with it. To compile a class follow command like below

  javac --enable-preview --release 14 <Path of Java Source File>

it will compile and create class. Now to run it simply use java command with enable preview.

If you want to leverage the JDK 11 feature where a single source file can be directly compile and run with single java command follow below command.

  java --enable-preview --source 14 <Path of Java Source File>
💖 💪 🙅 🚩
imagarg
Ashish Garg

Posted on February 26, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related