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

其他回答

... 从Java SE 8开始,局部类可以访问封闭块的final或有效final的局部变量和参数。在初始化后值从未改变的变量或参数实际上是final。

例如,假设变量numberLength没有声明为final,并且在PhoneNumber构造函数中添加了标记的赋值语句:

public class OutterClass {  

  int numberLength; // <== not *final*

  class PhoneNumber {

    PhoneNumber(String phoneNumber) {
        numberLength = 7;   // <== assignment to numberLength
        String currentNumber = phoneNumber.replaceAll(
            regularExpression, "");
        if (currentNumber.length() == numberLength)
            formattedPhoneNumber = currentNumber;
        else
            formattedPhoneNumber = null;
     }

  ...

  }

...

}

由于这个赋值语句,变量numberLength不再是有效的final。结果,Java编译器生成一个类似于“内部类引用的局部变量必须是final或有效的final”的错误消息,其中内部类PhoneNumber试图访问numberLength变量:

http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html

http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

' effective final'是一个变量,如果它被'final'附加,则不会产生编译器错误

在“Brian Goetz”的一篇文章中,

非正式地说,如果局部变量的初始值从未改变,那么它实际上就是final变量——换句话说,声明它为final不会导致编译失败。

lambda-state-final- Brian Goetz

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

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

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。