Search code examples
javajava-21ocpjp

FlowScoping in java 21


As I am preparing for OCPJP 21, came across Flow scoping topic.

i need some guidance understanding the concept. below is the code snippet

public class Question28 {
    
    static void getFish(Object fish) {            
        if(!(fish instanceof String guppy))
            System.out.println("Eat!");
        else if(!(fish instanceof String guppy)) {
            throw new RuntimeException();    
        }    
        System.out.println(guppy);    
        System.out.println("Swim!");
    }
    
    public static void main(String args[]) {    
        getFish("ashish");    
    } 
    
}
 

above snippet results into "Cannot find symbol" for variable guppy. but if i remove the else keyword, like below. guppy variable remains in scope. no compiler error.

static void getFish(Object fish) {  
    if(!(fish instanceof String guppy))
        System.out.println("Eat!");
     if(!(fish instanceof String guppy)) {
        throw new RuntimeException();    
    }    
    System.out.println(guppy);
}

my second observation as follows ref snippet

class Sample2 {
    
    static void printNumberTwice(Number number) {    
        if(!(number instanceof Integer data)) 
             return ;
            //System.out.println("Eat!");
    
        System.out.println(data.intValue());    
    }
    
    public static void main(String args[]) {    
        printNumberTwice(5);
    }
}

above snippet successfully compiles but if I comment out return and instead add SOP statement, it results into cannot find symbol for data.

please guide me to understand the concept.


Solution

  • You can only use the variable if the compiler can proof that the code is only executed if the pattern matches.

    Now, let's look at your example:

    if(!(fish instanceof String guppy))
        System.out.println("Eat!");
    else if(!(fish instanceof String guppy)) {
        throw new RuntimeException();
    }
    System.out.println(guppy);
    

    Let's say fish is not a String. In this case, it would enter the if statement printing Eat!. Since it entered the if, it doesn't enter the else if so it would continue trying to print guppy - But wait it is no String so guppy might not exist (it isn't definitely assigned).

    If you skip the else keyword, you are always throwing the exception if fish isn't a String. There, you only allow execution for Strings enduring that guppy is valid.

    Now, let's also check your second example:

    if(!(number instanceof Integer data)) 
        return;
        //System.out.println("Eat!");
    System.out.println(data.intValue());
    

    Here, you can use data because the compiler provably knows that number is an Integer. If it isn't, the execution flow would be cancelled/stopped by the return statement (which can also be done using exceptions, break; or continue;).

    If you remove the return and replace it with a statement not impacting control flow, passing non-Strings could cause the System.out.println to be executed.

    That being said, please note that the Java compiler is not a powerful theorem-prover and doesn't try to be. It does some work to detect whether variables are assigned but it isn't complete (which would be impossible thanks to Gödel) i.e. there are cases where it's impossible that a statement is executed but the compiler doesn't know. The important thing the compiler focuses is soundness i.e. ensuring that it doesn't allow invalid code (and being predictable and following the specification).