我在Java 8中使用lambda,我遇到警告,从lambda表达式引用的局部变量必须是final或有效的final。我知道当我在匿名类中使用变量时,它们在外部类中必须是final,但final和有效final之间的区别是什么?


当前回答

如果可以将最后一个修饰符添加到局部变量,那么就是 有效的决赛。

Lambda表达式可以访问

静态变量, 实例变量, 有效的最终 方法参数,以及 有效的最终 局部变量。

来源:OCP: Oracle认证专业Java SE 8程序员II学习指南,Jeanne Boyarsky, Scott Selikoff

此外,

有效最终变量是一个值为never的变量 更改了,但没有使用final关键字声明。

来源:从Java开始:从控制结构到对象(第6版),Tony Gaddis

此外,不要忘记final的含义,它在第一次使用之前只初始化一次。

其他回答

我发现解释“有效的final”最简单的方法是想象在变量声明中添加final修饰符。如果通过这个更改,程序在编译时和运行时继续以相同的方式运行,那么该变量实际上就是final。

When a lambda expression uses an assigned local variable from its enclosing space there is an important restriction. A lambda expression may only use local variable whose value doesn't change. That restriction is referred as "variable capture" which is described as; lambda expression capture values, not variables. The local variables that a lambda expression may use are known as "effectively final". An effectively final variable is one whose value does not change after it is first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error. Let's see it with an example, we have a local variable i which is initialized with the value 7, with in the lambda expression we are trying to change that value by assigning a new value to i. This will result in compiler error - "Local variable i defined in an enclosing scope must be final or effectively final"

@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;
        };
    }   
}

如果可以将最后一个修饰符添加到局部变量,那么就是 有效的决赛。

Lambda表达式可以访问

静态变量, 实例变量, 有效的最终 方法参数,以及 有效的最终 局部变量。

来源:OCP: Oracle认证专业Java SE 8程序员II学习指南,Jeanne Boyarsky, Scott Selikoff

此外,

有效最终变量是一个值为never的变量 更改了,但没有使用final关键字声明。

来源:从Java开始:从控制结构到对象(第6版),Tony Gaddis

此外,不要忘记final的含义,它在第一次使用之前只初始化一次。

有效的最后一个主题在JLS 4.12.4中进行了描述,最后一段包含了明确的解释:

如果变量实际上是final,在其声明中添加final修饰符将不会引入任何编译时错误。相反,在有效程序中声明为final的局部变量或参数,如果final修饰符被移除,则变为有效的final。

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; 

        }
    }    
}

正如其他人所说,在初始化后值从未改变的变量或参数实际上是final的。在上面的代码中,如果你在内部类FirstLevel中改变了x的值,那么编译器会给你一个错误消息:

从lambda表达式引用的局部变量必须是final或有效final。