1

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;
  }
}
Alessandro Argentieri
  • 2,901
  • 3
  • 32
  • 62

1 Answers1

4

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.

Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • I read: "An effectively final variable is one whose value does not change after it is first assigned." I assigned namePojo and its internal state and then changed it – Alessandro Argentieri Oct 25 '18 at 07:17
  • 2
    Plus, the value of an object reference is the referenced address, not the object itself. Thus the object behind the reference is modifiable. The reference itself is not modifiable, i.e. not reasignable. – Turing85 Oct 25 '18 at 07:18