lambda expression and global variables

10.4k views Asked by At

I just started working around lambda expression in Java 8. I'm bit confused about effectively final.

Local variable initialized once is effectively final but what about global variable, I can change value of global variable and use it in lambda expression. So what is the reason behind local variable should be effectively final. I couldn't find any articles or on javadoc about this.

import java.util.ArrayList;
import java.util.List;

public class Main {

    private String lastname = "Thakor";

    public static void main(String[] args) {
        Main objMain = new Main();
        objMain.test();
    }

    public void test(){
        List<String> listStrings = new ArrayList<String>();
        listStrings.add("Vicky");
        listStrings.add("Thakor");

        String middlename = "V";
        //Local variable middlename defined in an enclosing scope must be final or effectively final
        /* middlename = "T";*/ 

        /**
         * In case of global variable, why lambda expression not throwing error...
         * Local variable middlename defined in an enclosing scope must be final or effectively final
         */
        lastname = "T"; 

        listStrings.stream()
                        .forEach(firstname ->{
                            System.out.println("Firstname: " + firstname);
                            System.out.println("Middle: " + middlename);
                            System.out.println("Lastname: " + lastname);
                            System.out.println("--------------------------------------");
                        });
    }
}

Output

Firstname: Vicky
Middle: V
Lastname: T
--------------------------------------
Firstname: Thakor
Middle: V
Lastname: T
--------------------------------------
1

There are 1 answers

5
shmosel On BEST ANSWER

When you reference a "global" variable in a lambda, you're really capturing a reference to this, which is effectively final.

Consider the following snippet (untested):

class Main {

    class Box {
        int value;
    }

    void foo() {
        Box box = new Box();
        Runnable r = () -> box.value++;
    }
}

Even though you're changing the box.value, you're doing it through the box reference, which is effectively final. Similarly, you code is equivalent to

System.out.println("Lastname: " + this.lastname);

meaning you're accessing lastname through the this reference, which is effectively final.