Я играю с лямбдами в Java 8, и я столкнулся с предупреждением local variables referenced from a lambda expression must be final or effectively final
. Я знаю, что когда я использую переменные внутри анонимного класса, они должны быть финальными во внешнем классе, но все же - в чем разница между финальными и эффективно финальными ?
351
Ответы:
Например, предположим, что переменная
numberLength
не объявлена как финальная, и вы добавили отмеченный оператор присваивания вPhoneNumber
конструктор:Из-за этого оператора присваивания переменная numberLength больше не является окончательной. В результате компилятор Java генерирует сообщение об ошибке, похожее на «локальные переменные, на которые ссылается внутренний класс, должны быть окончательными или эффективно окончательными», когда внутренний класс PhoneNumber пытается получить доступ к переменной numberLength:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
источник
numberLength
стал локальной переменной этого метода.Я считаю, что самый простой способ объяснить «эффективно окончательный» - это представить добавление
final
модификатора к объявлению переменной. Если с этим изменением программа продолжает вести себя одинаково, как во время компиляции, так и во время выполнения, то эта переменная будет фактически финальной.источник
case k
требуется постоянное выражение, которое может быть константной переменной («Постоянная переменная является конечной переменной примитивного типа или типа String, которая инициализируется константным выражением» JLS 4.12.4 ), что является частным случаем финального переменная.Согласно документам :
По сути, если компилятор обнаруживает, что переменная не появляется в присваиваниях за пределами ее инициализации, то эта переменная считается фактически конечной .
Например, рассмотрим некоторый класс:
источник
bar
в вашем примере это не локальная переменная, а поле. «Эффективно окончательно» в сообщении об ошибке, как указано выше, не относится к полям вообще.bar
- это параметр, а не поле.'Effectively final' - это переменная, которая не выдаст ошибку компилятора, если она будет добавлена 'final'
Из статьи Брайана Гетца,
лямбда-государство-финал- Брайан Гетц
источник
Эта переменная ниже является окончательной , поэтому мы не можем изменить ее значение после инициализации. Если мы попытаемся, мы получим ошибку компиляции ...
Но если мы создадим такую переменную, мы можем изменить ее значение ...
Но в Java 8 все переменные являются окончательными по умолчанию. Но наличие 2-й строки в коде делает его не окончательным . Так что, если мы уберем 2 - й строки из кода выше, наша переменная теперь «эффективно окончательный» ...
Итак ... Любая переменная, которая назначается один раз и только один раз, является "эффективно конечной" .
источник
Переменная является окончательной или фактически конечной, когда она инициализируется один раз и никогда не мутирует в своем классе владельца. И мы не можем инициализировать его в циклах или внутренних классах .
Финал :
Эффективно финал :
источник
Когда лямбда-выражение использует назначенную локальную переменную из окружающего пространства, существует важное ограничение. Лямбда-выражение может использовать только локальную переменную, значение которой не изменяется. Это ограничение называется « захват переменной », который описывается как; лямбда-выражения захватывают значения, а не переменные .
Локальные переменные, которые может использовать лямбда-выражение, известны как « эффективно финальные ».
Фактически последняя переменная - это переменная, значение которой не изменяется после ее первого назначения. Нет необходимости явно объявлять такую переменную как final, хотя это не будет ошибкой.
Давайте рассмотрим это на примере, у нас есть локальная переменная i, которая инициализируется значением 7, и в лямбда-выражении мы пытаемся изменить это значение, присваивая новое значение i. Это приведет к ошибке компилятора - « Локальная переменная i, определенная во включающей области видимости, должна быть конечной или фактически конечной »
источник
Эффективная последняя тема описана в JLS 4.12.4, а последний абзац содержит четкое объяснение:
источник
final - объявить переменную с ключевым словом
final
, пример:это остается
final
через программу.фактически окончательный : любая локальная переменная или параметр, которому присваивается значение только один раз (или обновляется только один раз). Это может не оставаться эффективно окончательным в течение всей программы. таким образом, это означает, что эффективная конечная переменная может потерять свое фактически конечное свойство сразу после того, как она получит / обновит по крайней мере еще одно назначение. пример:
источник
final
ключевое слово в объявление, не внося ошибок компиляции, тогда оно не является окончательным . Это противоречит положению этого утверждения: «Если переменная является окончательной, то добавление модификатора final к ее объявлению не приведет к ошибкам во время компиляции».Как уже говорили другие, переменная или параметр, значение которых никогда не изменяется после инициализации, фактически является окончательным. В приведенном выше коде, если вы измените значение
x
во внутреннем классе,FirstLevel
то компилятор выдаст вам сообщение об ошибке:источник
Лямбда-выражения могут получить доступ
статические переменные,
переменные экземпляра,
эффективно окончательные параметры метода, и
эффективно финальные локальные переменные.
Источник: OCP: Oracle Certified Professional Java SE 8 Programmer II Учебное пособие, Жанна Боярски, Скотт Селикофф
Дополнительно,
Источник: Начиная с Java: от структур управления до объектов (6-е издание), Тони Гаддис
Кроме того, не забывайте о
final
том, что он инициализируется ровно один раз перед первым использованием.источник
Объявление переменной
final
или не объявление ееfinal
, но ее эффективное сохранение в конечном итоге может привести (зависит от компилятора) к другому байт-коду.Давайте посмотрим на небольшой пример:
Соответствующий байт-код
main
метода (Java 8u161 в Windows 64 Bit):Соответствующая таблица номеров строк:
Как мы видим , исходный код на линиях
12
,13
,14
не отображается в байт - код. Это потому чтоi
естьtrue
и не изменит его состояние. Таким образом, этот код недоступен (больше в этом ответе ). По той же причине код в строке9
тоже отсутствует. Состояниеi
не нужно оценивать, так как оноtrue
точно.С другой стороны, хотя переменная
j
является окончательной, она не обрабатывается таким же образом. Таких оптимизаций не применено. Состояниеj
оценивается два раза. Байт-код один и тот же, независимо от того,j
является ли он окончательным .источник
Окончательная переменная Effectively является локальной переменной, которая:
final
В то время как финальная переменная является переменной, которая:
final
ключевым словом.источник
Это не началось на Java 8, я использую это с давних времен. Этот код используется (до Java 8), чтобы быть законным:
источник
final
переменные могут быть доступны, но в Java 8 также те, которые фактически окончательным.