Java lambda 表达式中使用的变量应该是 final 或有效 final
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34865383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Variable used in lambda expression should be final or effectively final
提问by user3610470
Variable used in lambda expression should be final or effectively final
lambda 表达式中使用的变量应该是 final 或有效 final
When I try to use calTz
it is showing this error.
当我尝试使用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;
}
采纳答案by Francesco Pitzalis
A final
variable means that it can be instantiated only one time.
in Java you can't use non-final variables in lambda as well as in anonymous inner classes.
一个final
变量意味着它只能被实例化一次。在 Java 中,您不能在 lambda 以及匿名内部类中使用非最终变量。
You can refactor your code with the old for-each loop:
您可以使用旧的 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;
}
Even if I don't get the sense of some pieces of this code:
即使我不明白这段代码的某些部分:
- you call a
v.getTimeZoneId();
without using its return value - with the assignment
calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
you don't modify the originally passedcalTz
and you don't use it in this method - You always return
null
, why don't you setvoid
as return type?
- 你调用 a
v.getTimeZoneId();
而不使用它的返回值 - 通过分配,
calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
您不会修改最初传递的内容calTz
,也不会在此方法中使用它 - 你总是返回
null
,为什么不设置void
为返回类型?
Hope also these tips helps you to improve.
也希望这些提示可以帮助您改进。
回答by DMozzy
From a lambda, you can't get a reference to anything that isn't final. You need to declare a final wrapper from outside the lamda to hold your variable.
从 lambda 中,您无法获得对任何非最终内容的引用。您需要从 lamda 外部声明一个最终包装器来保存您的变量。
I've added the final 'reference' object as this wrapper.
我已经添加了最终的“引用”对象作为这个包装器。
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();
}
回答by Alexander Udalov
In your example, you can replace the forEach
with lamdba with a simple for
loop and modify any variable freely. Or, probably, refactor your code so that you don't need to modify any variables. However, I'll explain for completeness what does the error mean and how to work around it.
在您的示例中,您可以forEach
用一个简单的for
循环替换with lamdba并自由修改任何变量。或者,可能重构您的代码,以便您不需要修改任何变量。但是,为了完整起见,我将解释错误的含义以及如何解决它。
Java 8 Language Specification, §15.27.2:
Java 8 语言规范,第15.27.2 节:
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
任何使用但未在 lambda 表达式中声明的局部变量、形式参数或异常参数必须声明为 final 或有效 final ( §4.12.4),否则在尝试使用时会发生编译时错误。
Basically you cannot modify a local variable (calTz
in this case) from within a lambda (or a local/anonymous class). To achieve that in Java, you have to use a mutable object and modify it (via a final variable) from the lambda. One example of a mutable object here would be an array of one element:
基本上你不能calTz
从 lambda(或本地/匿名类)中修改局部变量(在这种情况下)。要在 Java 中实现这一点,您必须使用可变对象并从 lambda 修改它(通过最终变量)。此处可变对象的一个示例是一个包含一个元素的数组:
private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
TimeZone[] result = { null };
try {
cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
...
result[0] = ...;
...
}
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return result[0];
}
回答by Dinesh Arora
Java 8has a new concept called “Effectively final” variable. It means that a non-final local variable whose value never changes after initialization is called “Effectively Final”.
Java 8有一个新概念,称为“Effectively final”变量。这意味着初始化后值永远不会改变的非最终局部变量称为“有效最终”。
This concept was introduced because prior to Java 8, we could not use a non-final local variable in an anonymous class. If you wanna have access to a local variable in anonymous class, you have to make it final.
引入这个概念是因为在Java 8之前,我们不能在匿名类中使用非最终局部变量。如果您想访问匿名类中的局部变量,则必须使其成为最终变量。
When lambda was introduced, this restriction was eased. Hence to the need to make local variable final if it's not changed once it is initialized as lambda in itself is nothing but an anonymous class.
当引入 lambda 时,这个限制就放宽了。因此,如果局部变量在初始化为 lambda 后没有更改,则需要将其设为 final,因为 lambda 本身只不过是一个匿名类。
Java 8realized the pain of declaring local variable final every time a developer used lambda, introduced this concept, and made it unnecessary to make local variables final. So if you see the rule for anonymous classes has not changed, it's just you don't have to write the final
keyword every time when using lambdas.
Java 8意识到每次开发人员使用 lambda 时都声明局部变量 final 的痛苦,引入了这个概念,并使得局部变量没有必要将其设为 final。所以如果你看到匿名类的规则没有改变,只是你final
在使用 lambda 时不必每次都写关键字。
I found a good explanation here
我在这里找到了一个很好的解释
回答by robie2011
if it is not necessary to modify the variable than a general workaround for this kind of problem would be toextract the part of code which use lambda and use final keyword on method-parameter.
如果没有必要修改变量,那么对于此类问题的一般解决方法是提取使用 lambda 的代码部分,并在方法参数上使用 final 关键字。
回答by Vince Emigh
Although other answers prove the requirement, they don't explain whythe requirement exists.
虽然其他答案证明的要求,他们没有解释为什么要求存在。
The JLS mentions why in §15.27.2:
JLS 在§15.27.2 中提到了原因:
The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems.
对有效最终变量的限制禁止访问动态变化的局部变量,其捕获可能会引入并发问题。
To lower risk of bugs, they decided to ensure captured variables are never mutated.
为了降低出现错误的风险,他们决定确保捕获的变量永远不会发生变异。
回答by Andreas Foteas
A variable used in lambda expression should be a final or effectively final, but you can assign a value to a final one element array.
lambda 表达式中使用的变量应该是 final 或有效的 final,但您可以为 final 一个元素数组赋值。
private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
try {
TimeZone calTzLocal[] = new TimeZone[1];
calTzLocal[0] = calTz;
cal.getComponents().get("VTIMEZONE").forEach(component -> {
TimeZone v = component;
v.getTimeZoneId();
if (calTzLocal[0] == null) {
calTzLocal[0] = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
}
});
} catch (Exception e) {
log.warn("Unable to determine ical timezone", e);
}
return null;
}