2016-06-24 3 views
15

У меня есть анонимный внутренний класс и эквивалентная лямбда. Почему правила инициализации переменных более строгие для лямбда, и есть ли решение более чистое, чем анонимный внутренний класс, или инициализация его в конструкторе?Java lambdas имеют разные переменные требования, чем анонимные внутренние классы

import java.util.concurrent.Callable; 

public class Immutable { 
    private final int val; 

    public Immutable(int val) { this.val = val; } 

    // Works fine 
    private final Callable<String> anonInnerGetValString = new Callable<String>() {  
     @Override 
     public String call() throws Exception { 
      return String.valueOf(val); 
     } 
    }; 

    // Doesn't compile; "Variable 'val' might not have been initialized" 
    private final Callable<String> lambdaGetValString =() -> String.valueOf(val); 
} 

Edit: Я побежал через один обходной путь: используя поглотитель для val.

+1

Связанные: http://stackoverflow.com/questions/30130148/reference-to-the-final-field-from-lambda-expression –

+2

Интересно, что если вы снимаете 'final' модификатор на 'val' он компилирует ... – lucasvw

+0

Когда вы меняете' String.valueOf (val) 'на' String.valueOf (Immutable.this.val) 'он тоже компилируется – csharpfolk

ответ

7

В главе о lambda expression bodies говорится

В отличие от кода появляются в объявлениях анонимных классов, смысл имен и this и super ключевые слова, входящие в лямбда тела, наряду с доступностью ссылочных деклараций, одинаковы как в окружении (за исключением того, что параметры лямбда вводят новых имен).

Прозрачность this в теле выражения в лямбды (как явного и неявного) - то есть, рассматривая его так же, как и в окружающего контекста - обеспечивает большую гибкость для реализаций, и предотвращает значение неквалифицированного имена в теле от в зависимости от разрешения перегрузки.

Они более строгие из-за этого.

Окружающий контекст, в данном случае, является присвоением полю, а проблема под рукой - это доступ к полю, val, пустое поле final, в правой части выражения.

Спецификация языка Java утверждает

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

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

Для каждого доступа к локальной переменной или пустой final поле x, x должен быть определенно присвоенной перед въездом, или во время компиляции происходит ошибка.

Затем он продолжает говорить

Пусть C быть классом, и пусть V быть пустым final не- static элемент поля из C, объявленная в C. Тогда:

  • V определенно Unassigned (и к тому же не обязательно назначается) до крайнего левого экземпляра инициализатора (§8.6) или переменного экземпляр инициализатора C.

  • V это [не] установленный перед экземпляром инициализатора или переменный экземпляром инициализатором C отличных от крайней левой тогда и только тогда V является [ип] установленный после предыдущего экземпляра инициализатора или экземпляра переменного инициализатора C.

Ваш код в основном выглядит следующим образом

private final int val; 
// leftmost instance variable initializer, val still unassigned 
private final Callable<String> anonInnerGetValString = ... 
// still unassigned after preceding variable initializer 
private final Callable<String> lambdaGetValString = ... 

поэтому компилятор определяет, что val в Unassigned, когда это доступ в выражении инициализации для lambdaGetValString.

Приведенные выше правила применяются к использованию простого имени, val, а не к квалифицированному выражению, this.val. Вы можете использовать

final Callable<String> lambdaGetValString =() -> String.valueOf(this.val); 
3

Это не будет компилироваться:

public class Example 
{ 
    private final int x; 
    private final int y = 2 * x; 

    public Example() { 
    x = 10; 
    } 
} 

, но это будет:

public class Example 
{ 
    private final int x; 
    private final int y; 

    public Example() { 
    x = 10; 
    y = 2 * x; 
    } 
} 

и так это будет:

public class Example 
{ 
    private final int x = 10; 
    private final int y = 2 * x; 
} 

Так это не имеет ничего общего с лямбды. Поле, которое инициализируется на той же строке, в которой оно объявлено, оценивается до выполнения конструктора. Итак, в этот момент переменная 'val' (или в этом примере 'x') не была инициализирована.

+0

Определите _same line_. –

Смежные вопросы