Поведение поставщика Java 8: конечная переменная не может быть инициализирована

Java запрещает использование конечной переменной внутри поставщика, поскольку она не может быть инициализирована, но добавляет "(это)". переменная заставляет ее компилироваться и работать нормально.

Кроме того, вызов такого поставщика приводит к ошибке NullPointerException вместо ошибки компилятора, если он вызывается перед назначением переменной и выполняется как ожидается, если вызвано после.

Описанное здесь поведение?

Я использую OpenJDK 1.8.0_151.

Пример:

import java.util.function.Supplier;
class Example {
  final String str;

  Supplier<Integer> test1 = () -> str.length();        // DOES NOT COMPILE
  Supplier<Integer> test2 = () -> this.str.length();   // DOES NOT COMPILE
  Supplier<Integer> test3 = () -> (this.str).length(); // DOES NOT COMPILE
  Supplier<Integer> test4 = () -> (this).str.length(); // OK

  Example(String str) {
    System.out.println(test4.get()); // NullPointerException
    this.str = str;
    System.out.println(test4.get()); // OK
  }
}

---

javac Example.java

Example.java:7: error: variable str might not have been initialized
Supplier<Integer> test1 = () -> str.length();        // DOES NOT COMPILE
                                ^
Example.java:8: error: variable str might not have been initialized
Supplier<Integer> test2 = () -> this.str.length();   // DOES NOT COMPILE
                                    ^
Example.java:9: error: variable str might not have been initialized
Supplier<Integer> test3 = () -> (this.str).length(); // DOES NOT COMPILE
                                     ^
3 errors

Ответ 1

От JLS версия 9, глава 16:

Каждая локальная переменная (§14.4) и каждое пустое конечное поле (§4.12.4, §8.3.1.2) должен иметь определенно присвоенное значение, когда любой доступ к его значение имеет значение.

Доступ к его значению состоит из простого имени переменной (или для поля, простое имя поля, квалифицируемого этим) происходящее где угодно в выражении, кроме как левый операнд простой оператор присваивания = (§15.26.1).

str - это простое имя конечного поля, а this.str - простое имя поля, имеющего квалификацию this. (this).str не относится ни к одному из этих случаев ((this) не считается "квалифицированным с помощью this" ), поэтому он не считается доступом.