Alessandro Argentieri
Alessandro Argentieri

Reputation: 3215

Lambda Expression works with no effectively final variable

I've read that the external variables which a lambda expression uses must be final or effectively final. If I try to modify an external String value in the body of a Supplier, for instance, the compiler blocks me, as by the definition above. But if I use an external Pojo (modifying its attribute - so its internal state), then it works correctly and negate the declaration above.

How comes?

package com.quicktutorialz.test;

import java.util.function.Supplier;

public class MainApplication {

  public static void main(String[] args){

    // MY NON-EFFECTIVELY-FINAL POJO
    NamePojo namePojo = new NamePojo();
    namePojo.setName("Luisa");

    //MY LAMBDA
    Supplier<String> supplier = () -> {
        namePojo.setName("Alex");  //HOW IS THAT POSSIBLE?!?!
        return "Hello " + namePojo.getName();
    };

    System.out.println(supplier.get());


  }
}

class NamePojo {
  String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

Upvotes: 1

Views: 87

Answers (1)

Olivier Gr&#233;goire
Olivier Gr&#233;goire

Reputation: 35427

The variable is (effectively) final, its fields are not.

You're mixing a final variable and an immutable one.

A final variable means that it cannot be reassigned. Example:

void doSomething() {
  int a = 0;  // is effectively final
  int b = 1;  // is not effectively final
  b = 2;
}

An immutable variable means that its external representation won't change. This mostly means that its fields are final or effectively final. Example:

class A {
  int value;
  A(int value) { this.value = value; }
  int getValue() { return this.value; }
}

public void doSomething() {
  A a = new A(0);
  // no way to change a.value
}

In your case:

public class MainApplication {

  public static void main(String[] args){

    // MY NON-EFFECTIVELY-FINAL POJO
    NamePojo namePojo = new NamePojo();    // namePojo is assigned.
    namePojo.setName("Luisa");             // namePojo is changed, but not reassigned.

    //MY LAMBDA
    Supplier<String> supplier = () -> {
      // namePojo is changed, but not reassigned.
      namePojo.setName("Alex");  //HOW IS THAT POSSIBLE?!?!
      return "Hello " + namePojo.getName();
    };

    System.out.println(supplier.get());

  }
}

The content of your variable namePojo is changed, but the variable itself is never reassigned, since it's not reassigned, it's effectively final. The reference to namePojo never changed in your code, making it effectively final.

Upvotes: 4

Related Questions