最后的和有效的最终的区别
我在Java 8中玩lambdaexpression式,并且遇到了local variables referenced from a lambda expression must be final or effectively final
警告。 我知道当我在匿名类中使用variables的时候,他们在外部类中必须是最终的,但是仍然是 – 最后的和有效的最终的区别是什么?
…从Java SE 8开始,本地类可以访问封闭块的局部variables和参数,这些variables和参数是最终的或有效的最终的。 初始化后永远不会改变其值的variables或参数实际上是最终的。
例如,假设variablesnumberLength没有声明为final,并且在PhoneNumber构造函数中添加了标记的赋值语句:
PhoneNumber(String phoneNumber) { numberLength = 7; // <== assignment to numberLength String currentNumber = phoneNumber.replaceAll( regularExpression, ""); if (currentNumber.length() == numberLength) formattedPhoneNumber = currentNumber; else formattedPhoneNumber = null; }
由于这个赋值语句,variablesnumberLength不再有效。 因此,Java编译器会生成类似于“内部类引用的局部variables必须是最终或有效最终”的错误消息,其中内部类PhoneNumber尝试访问numberLengthvariables:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
我觉得最简单的方法来解释“有效的最终”是想象在variables声明中添加final
修饰符。 如果通过这种改变,程序在编译时和运行时都继续以相同的方式运行,那么这个variables就是有效的。
[编辑:Henno Vermeulen,在下面的评论中指出了这个规则的一个小例外。 该规则仍然非常简单而有用,但是语法规则意味着它不是普遍适用的。]
根据文件 :
初始化后永远不会改变其值的variables或参数实际上是最终的。
基本上,如果编译器发现一个variables没有出现在初始化之外的赋值中,那么这个variables被认为是有效的 。
例如,考虑一些类:
public class Foo { public void baz(int bar) { // While the next line is commented, bar is effectively final // and while it is uncommented, the assignment means it is not // effectively final. // bar = 2; } }
从“布赖恩·戈茨”的一篇文章中,
“有效最终”是一个variables,如果它被附加到“最终”
lambda状态最后 – 布赖恩·戈茨
variables在初始化一次时是最终的或有效的最终 的,并且在其所有者类中不会变异 。 而且我们不能在循环或内部类中 初始化它。
决赛 :
final int number; number = 23;
有效决赛 :
int number; number = 34;
当lambdaexpression式使用从其封闭空间分配的局部variables时,有一个重要的限制。 lambdaexpression式只能使用其值不变的局部variables。 该限制被称为“ 可变捕获 ”,其被描述为: lambdaexpression式捕获值,而不是variables 。
lambdaexpression式可能使用的局部variables被称为“ 有效最终 ”。
有效的最终variables是一个值在第一次赋值后不会改变的variables。 没有必要明确地声明这样的variables为final,尽pipe这样做不会是错误的。
我们来看一个例子,我们有一个局部variablesi,它用值7初始化,在lambdaexpression式中我们试图通过给i赋一个新的值来改变这个值。 这将导致编译器错误 – “ 在封闭范围中定义的本地variables必须是最终的或有效的最终的 ”
@FunctionalInterface interface IFuncInt { int func(int num1, int num2); public String toString(); } public class LambdaVarDemo { public static void main(String[] args){ int i = 7; IFuncInt funcInt = (num1, num2) -> { i = num1 + num2; return i; }; } }
下面这个variables是final的 ,所以一旦初始化,我们不能改变它的值。 如果我们尝试我们会得到一个编译错误…
final int variable = 123;
但是,如果我们创build一个这样的variables,我们可以改变它的价值…
int variable = 123; variable = 456;
但在Java 8中 ,所有variables默认都是最终的。 但是代码中第二行的存在使得它不是最终的 。 所以如果我们从上面的代码中删除第二行,我们的variables现在是“有效的最终” …
int variable = 123;
所以, 任何一次只能分配一次的variables都是“有效的” 。
public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { // The following statement causes the compiler to generate // the error "local variables referenced from a lambda expression // must be final or effectively final" in statement A: // // x = 99; } } }
正如其他人所说,一个variables或参数的值在初始化后永远不会改变,这是有效的。 在上面的代码中,如果在内部类FirstLevel
更改x
的值,则编译器会给出错误消息:
从lambdaexpression式引用的局部variables必须是最终的或有效的最终的。
如果可以将
final
修饰符添加到局部variables,那么它是有效的。
Lambdaexpression式可以访问
-
静态variables,
-
实例variables,
-
有效的最终方法参数和
-
有效的最终局部variables。
来源: OCP:Oracleauthentication专业Java SE 8 Programmer II学习指南,Jeanne Boyarsky,Scott Selikoff
但是,从Java SE 8开始,本地类可以访问最终或有效最终的>封闭块的本地variables和参数。
这并没有从Java 8开始,我很久以来就使用它了。 这个代码使用(在Java 8之前)是合法的:
String str = ""; //<-- not accesible from anonymous classes implementation final String strFin = ""; //<-- accesible button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String ann = str; // <---- error, must be final (IDE's gives the hint); String ann = strFin; // <---- legal; String str = "legal statement on java 7," +"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl."; //we are forced to use another name than str } );