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


当前回答

Final是一个用关键字Final声明的变量,示例:

final double pi = 3.14 ;

它在整个程序中都是最终的,在这一行之后改变PI是不允许的。

有效的最终的:任何局部变量或参数,现在只被赋值一次(或只更新一次)。在整个项目中,它可能不是有效的最终结果。这意味着有效最终变量可能会在被分配/更新至少一次赋值后立即失去其有效最终属性。例子:

class EffectivelyFinal {
    
    public static void main(String[] args) {
        calculate(124,53);
    }
    
    public static void calculate( int operand1, int operand2){   
     int rem = 0;  //   operand1, operand2 and rem are effectively final here
     rem = operand1%2  // rem lost its effectively final property here because it gets its second assignment 
                       // operand1, operand2 are still effectively final here 
        class operators{

            void setNum(){
                operand1 =   operand2%2;  // operand1 lost its effectively final property here because it gets its second assignment
            }
            
            int add(){
                return rem + operand2;  // does not compile because rem is not effectively final
            }
            int multiply(){
                return rem * operand1;  // does not compile because both rem and operand1 are not effectively final
            }
        }   
   }    
}

其他回答

下面这个变量是最终的,所以我们不能改变它的值一旦初始化。如果我们尝试,我们会得到一个编译错误…

final int variable = 123;

但是如果我们创建一个这样的变量,我们可以改变它的值…

int variable = 123;
variable = 456;

但是在Java 8中,默认情况下所有变量都是final。但是代码中第二行的存在使得它不是最终的。因此,如果我们从上面的代码中删除第二行,我们的变量现在是“有效的final”…

int variable = 123;

所以. .任何赋值一次且仅一次的变量都是“有效的最终变量”。

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。

根据文件:

在初始化后值从未改变的变量或参数实际上是final。

基本上,如果编译器发现一个变量在初始化之外没有出现在赋值中,则认为该变量实际上是final变量。

例如,考虑一些类:

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

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

当一个变量被初始化一次并且在其所有者类中从未发生变化时,它就是最终的或有效的最终的。我们不能在循环或内部类中初始化它。

最后:

final int number;
number = 23;

有效的最后:

int number;
number = 34;

注意:Final和Effective Final是相似的(赋值后它们的值不会改变),但只是有效的Final变量 没有使用关键字final声明。