Showing posts with label instanceof. Show all posts
Showing posts with label instanceof. Show all posts

Sunday, February 28, 2021

Using binding variable in the expression of if statement

Yes. We can use the binding variable inside of an if expression as long as its assured that when the code flow reaches that expression its assured that the instanceof operator has evaluated to true

Below sample code illustrates this


interface Pet {
    default public String color() {
        return this.color();
    }
}

record Dog(String color) implements Pet {

    public void bark() {
        System.out.println("I bark...");
    }
}

record Cat(String color) implements Pet {

    public void meaw() {
        System.out.println("I meaw...");
    }
}

public class Main {

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (p1 instanceof Dog d && d.color().equals("White") && 
                p2 instanceof Cat c && c.color().equals("White")) {
            d.bark();
            c.meaw();
        }
    }
}

Here we are using the binding variables c & d inside of an if expression. Note that the usage is allowed when used with short-circuit && operator, which makes sure the subsequent expression gets evaluated only when the 1st condition evaluates to true. 


Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/In-if-expression

Binding variable scope with NOT (!) condition

Let us introduce a NOT (!) operator to the if condition and study the scope of the binding variable in this case. 

Full sample code with NOT condition shown below  


interface Pet {
    default public String color() {
        return this.color();
    }

}

record Dog(String color) implements Pet {

    public void bark() {
        System.out.println("I bark...");
    }
}

record Cat(String color) implements Pet {

    public void meaw() {
        System.out.println("I meaw...");
    }
}

public class Main {

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (!(p1 instanceof Dog d)) {
            return;
        }
        
        if (!(p2 instanceof Cat c)) {
            return;
        }

        d.bark();
        c.meaw();
    }
}

This code compiles and executes as expected. 

Here binding variables are not accessible inside of the if block because of the not condition and are accessible outside of the if blocks.  

If you observe the code carefully, we are returning immediately if p1 is not Dog and p2 is not Cat. Code flow reaches beyond the if statements if and only if d is Dog and c is Cat. 

It is this unconditional return statement inside of the if block that makes this code work. Had the main method been like this instead, it will not compile

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (!(p1 instanceof Dog d)) {
            System.out.println("p1 is not Dog");
        }
        
        if (!(p2 instanceof Cat c)) {
            System.out.println("p2 is not Cat");
        }

        d.bark();
        c.meaw();
    }

This code will not compile as the flow would reach d.bark() and c.meaw() even when d and c are not Dog & Cat respectively.

Finally in the below code, we use the binding variable in the else part of the if condition with NOT (!) operator. Here, its assured that d and c are Dog and Cat when the flow reaches their respective else blocks 

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (!(p1 instanceof Dog d)) {
            System.out.println("p1 is not Dog");
        } else {
            d.bark();
        }
        
        if (!(p2 instanceof Cat c)) {
            System.out.println("p2 is not Cat");
        } else {
            c.meaw();
        }
    }

This will compile and execute as expected.

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/With-NOT-condition

Scoping of binding variables

Binding variables of instanceof operator is visible only inside of the code blocks that are reachable when the instanceof operator evaluates to true. 

Below sample illustrates this.


interface Pet {
    default public String color() {
        return this.color();
    }

}

record Dog(String color) implements Pet {

    public void bark() {
        System.out.println("I bark...");
    }
}

record Cat(String color) implements Pet {

    public void meaw() {
        System.out.println("I meaw...");
    }
}

public class Main {

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (p1 instanceof Dog d) {
            d.bark();
        }

        if (p2 instanceof Cat c) {
            c.meaw();
        }

        d.bark(); 
        c.meaw(); // Will not compile. c & d are not visible beyond their respective if blocks
    }
}

In the above sample, lines c.meaw()  and d.bark() outside of the if statement will not compile, as at this point c & d are not visible. 

Now we will make a few changes to the above code to study the scope of binding variables in-depth. 

First lets try to move the instanceof operator with binding variable outside of the if block. The main method is changed as shown below

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        boolean isDog = p1 instanceof Dog d;
        boolean isCat = p2 instanceof Cat c;

        d.bark();
        c.meaw();

    }

In the code above, it looks like c & d should be visible through the rest of the main() method. But this code also fails to compile. Binding variables c & d are not visible beyond the line containing their respective instanceof operator 

The code block that is reachable, when the instanceof operator evaluates to true is empty and its scope ends with assigning its result to the boolean variable. The code lines d.bark() and c.meaw() gets executed irrespective of if the instance of operator evaluates to true of false 

This explains why the binding variables are not visible beyond their respective instanceof operator statements. Easy!!! Below scenario gets a bit more tricky

Now lets modify the code to use both the instanceof binding inside of an if block, combining the two instance of operator with a logical & operator as shown below

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (p1 instanceof Dog d & p2 instanceof Cat c) {
            d.bark();
            c.meaw();
        }
    }

Phew... the code still doesn't compile. Binding variables c and d are not visible inside of the if block :(

Since the logical AND (&) operator is not short-circuiting and the second condition gets executed even which the first check evaluates to false, the scope of the binding variable is not extended beyond the instanceof operator statement and hence not visible inside of the if block!!! Bit tricky, but thats how the scope of the binding variable is...

And now, lets try with the short circuit AND operator (&&) as shown below

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        if (p1 instanceof Dog d && p2 instanceof Cat c) {
            d.bark();
            c.meaw();
        }
    }

This time, code compiles and executes as expected. Every piece of code beyond the instanceof binding variable assignment gets executed if and only if the instance of operator evaluates to true. This makes the binding variables c and d visible inside of the if block. 

And there is another interesting case on the scope of binding variable when used with the NOT (!) operator. We will explore that in the next post.


Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/Scoping

A complete sample for binding variable usage

Below is a complete sample code illustrating the usage of binding variables with instanceof operator

In the below code, we have an interface Pet which is implemented by two record classes - Cat & Dog. Dog has an instance method bark() and Cat has an instance method meaw().

In the main() method of the Main class, we proceed to create Pet instances for Dog and Cat and proceed to show usage of instanceof operator with & without the usage of binding variable


interface Pet {
    default public String color() {
        return this.color();
    }
}

record Dog(String color) implements Pet {

    public void bark() {
        System.out.println("I bark...");
    }
}

record Cat(String color) implements Pet {

    public void meaw() {
        System.out.println("I meaw...");
    }
}

public class Main {

    public static void main(String[] args) {

        Pet p1 = new Dog("White");
        Pet p2 = new Cat("White");

        // Without using binding variable
        if (p1 instanceof Dog) {
            Dog d = (Dog) p1;
            d.bark();
        }

        // With binding variable
        if (p2 instanceof Cat c) {
            c.meaw();
        }
        
    }
}


Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/A-complete-sample

Friday, February 26, 2021

Binding variable for instanceof operator

We know that instanceof operator tests a variable for a particular type. This is generally used when we have a variable of a super type and we want to check if its of a specific subtype before casting and invoking specific methods or attributes of that subtype

Here is the sample of instanceof usage

    public int getSizeOfString(Object o) {
        if (o instanceof String) {
            String s = (String) o;
            return s.length();
        } else {
            return 1;
        }
    }

We check if o is of type String. If so, we cast it to String, assign it to the variable s and then proceed to return the length of the string. Otherwise we return 1 as the default size.

Binding variable for instanceof operator introduced in Java 15, makes this casting and assignment to the local variable much more concise and less error prone. 

Below is this code using binding variable with instanceof operator

    public int getSizeOfString(Object o) {
        if (o instanceof String s) {
            return s.length();
        } else {
            return 1;
        }
    }

In the above code, s is the binding variable used with the instanceof operator. If Object o is of type String, it is cast to String and assigned to s as part of the single line statement. We can proceed to use s as a String variable holding the object assigned inside of the if block. 

As you can see, not only does this make the code concise, it also eliminates the possibility for errors due to wrong manual casting.

We will explore more scenarios of using binding variable with instanceof operator in the next few posts. 

A complete sample for binding variable usage

Scoping of binding variables

Binding variable scope with NOT (!) condition

Using binding variable in the expression of if statement


Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/Usage