最后的和有效的最终的区别

我在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 } );