Вопрос-ответ

Variable used in lambda expression should be final or effectively final

Переменная, используемая в лямбда-выражении, должна быть окончательной или эффективно окончательной

Переменная, используемая в лямбда-выражении, должна быть окончательной или эффективно окончательной


Когда я пытаюсь использовать calTz, она показывает эту ошибку.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
try {
cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
VTimeZone v = (VTimeZone) component;
v.getTimeZoneId();
if (calTz == null) {
calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
}
});
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return null;
}
Переведено автоматически
Ответ 1

Хотя другие ответы подтверждают требование, они не объясняют, почему это требование существует.

JLS упоминает, почему в § 15.27.2:


Ограничение на переменные effectively final запрещает доступ к динамически изменяющимся локальным переменным, захват которых, вероятно, приведет к проблемам параллелизма.


Чтобы снизить риск ошибок, они решили гарантировать, что захваченные переменные никогда не будут изменены.


Это также применимо к анонимным внутренним классам

Ответ 2

A final переменная означает, что ее экземпляр может быть создан только один раз. в Java вы не можете переназначать неокончательные локальные переменные в lambda, а также в анонимных внутренних классах.

Вы можете реорганизовать свой код с помощью старого цикла for-each:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
try {
for(Component component : cal.getComponents().getComponents("VTIMEZONE")) {
VTimeZone v = (VTimeZone) component;
v.getTimeZoneId();
if(calTz==null) {
calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
}
}
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return null;
}

Даже если я не понимаю смысла некоторых фрагментов этого кода:


  • вы вызываете a v.getTimeZoneId(); без использования ее возвращаемого значения

  • при присваивании calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); вы не изменяете первоначально переданную calTz переменную и не используете ее в этом методе

  • Вы всегда возвращаете null, почему бы вам не установить void в качестве возвращаемого типа?

Надеюсь, что эти советы также помогут вам улучшить работу.

Ответ 3

Из лямбда-выражения вы не можете получить ссылку на что-либо, что не является окончательным. Вам нужно объявить конечную оболочку извне lamda для хранения вашей переменной.

Я добавил окончательный объект 'reference' в качестве этой оболочки.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
final AtomicReference<TimeZone> reference = new AtomicReference<>();

try {
cal.getComponents().getComponents("VTIMEZONE").forEach(component->{
VTimeZone v = (VTimeZone) component;
v.getTimeZoneId();
if(reference.get()==null) {
reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue()));
}
});
} catch (Exception e) {
//log.warn("Unable to determine ical timezone", e);
}
return reference.get();
}
Ответ 4

ВJava 8 появилась новая концепция, называемая переменной “Effectively final” . Это означает, что неокончательная локальная переменная, значение которой никогда не меняется после инициализации, называется “Effectively Final” .

Эта концепция была введена потому, что до Java 8 мы не могли использовать локальную переменную, не являющуюся окончательной, в анонимном классе. Если вы хотите иметь доступ к локальной переменной в анонимном классе, вы должны сделать ее окончательной.

Когда был представлен lambda, это ограничение было ослаблено. Следовательно, необходимо сделать локальную переменную final, если она не изменяется после инициализации, поскольку lambda сама по себе является не чем иным, как анонимным классом.

Java 8 осознала сложность объявления локальной переменной final каждый раз, когда разработчик использует lambda, представила эту концепцию и избавила от необходимости делать локальные переменные final . Итак, если вы видите, что правило для анонимных классов не изменилось, просто вам не нужно каждый раз писать final ключевое слово при использовании лямбд.

Я нашел хорошее объяснение здесь

java lambda