最终和有效决赛之间的区别

ale*_*lex 329 java lambda final inner-classes java-8

我在Java 8中玩lambdas,我遇到了警告local variables referenced from a lambda expression must be final or effectively final.我知道当我在匿名类中使用变量时,它们必须在外部类中是最终的,但仍然 - 最终有效最终之间有什么区别?

Sur*_*tta 220

...从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数.在初始化之后其值永远不会改变的变量或参数实际上是最终的.

例如,假设变量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;
     }

  ...

  }

...

}
Run Code Online (Sandbox Code Playgroud)

由于这个赋值语句,变量numberLength不再是最终的.因此,Java编译器生成类似于"从内部类引用的局部变量必须是final或者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

  • +1注意:如果引用未更改,即使引用的对象发生更改,它也是最终的. (65认同)
  • 该示例不正确。这段代码可以完美编译(当然没有点)。要获得编译器错误,此代码应该位于某个方法内,以便“numberLength”成为此方法的局部变量。 (3认同)
  • 这个例子如此复杂有什么原因吗?为什么大部分代码处理完全不相关的正则表达式操作?而且,正如 @mykola 已经说过的那样,它完全缺少关于 * effective final * 属性的标记,因为它只与局部变量相关,并且在这个例子中没有局部变量。 (2认同)

Mau*_*lin 123

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

  • 此规则的一个例外是使用常量初始化的局部变量不是编译器的常量表达式.在明确添加final关键字之前,不能在switch/case中使用这样的变量.例如"int k = 1; switch(someInt){case k:...". (8认同)
  • 这是事实,只要对java 8的"最终"的理解是很好理解的.否则,我会查看一个未被声明为final的变量,您之后进行了赋值,并错误地认为它不是最终的.你可能会说"当然"......但并不是每个人都应该尽可能多地关注最新的语言版本变化. (4认同)
  • @HennoVermeulen switch-case不是本答案中规则的例外.该语言指定`case k`需要_constant expression_,它可以是_constant variable_("常量变量是基本类型的最终变量或用常量表达式初始化的String类型"[JLS 4.12.4](https: //docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.4))这是最终变量的特例. (2认同)
  • 在我的示例中,编译器抱怨k不是常数表达式,因此不能将其用于开关。添加final时,编译行为会更改,因为它现在是一个常量变量,可以在开关中使用。因此,您是对的:规则仍然正确。它根本不适用于此示例,也没有说k是否有效地为最终值。 (2认同)

Mar*_*iot 34

根据文件:

在初始化之后其值永远不会改变的变量或参数实际上是最终的.

基本上,如果编译器发现变量没有出现在其初始化之外的赋值中,那么该变量被认为是最终的.

例如,考虑一些类:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @AnttiHaapala`bar`是这里的参数,而不是字段. (6认同)

Aje*_*nga 26

来自'Brian Goetz'的文章,

'Effectively final'是一个变量,如果它被'final'附加,它将不会给编译器错误

lambda-state-final- Brian Goetz

  • 此答案显示为引号,但是Brian的文章中没有这样的确切文字,请确保没有_appended_这个词。而是用引号引起来:_非正式地,如果局部变量的初始值从未改变,则它实际上是最终的-换句话说,声明为最终不会导致编译失败。 (2认同)

Eur*_*nes 22

下面的这个变量是最终的,所以我们不能在初始化后改变它的值.如果我们试图得到编译错误......

final int variable = 123;
Run Code Online (Sandbox Code Playgroud)

但是如果我们创建这样的变量,我们可以改变它的价值......

int variable = 123;
variable = 456;
Run Code Online (Sandbox Code Playgroud)

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

int variable = 123;
Run Code Online (Sandbox Code Playgroud)

所以.. 任何一次只分配一次的变量就是"有效的最终".

  • @Eurig,“所有变量默认都是最终的”需要引用。 (3认同)

sam*_*adi 8

当一个变量初始化一次并且它的所有者类中从未变异,变量是最终的有效的最终变量.我们不能循环内部类中初始化它.

最后:

final int number;
number = 23;
Run Code Online (Sandbox Code Playgroud)

有效最终:

int number;
number = 34;
Run Code Online (Sandbox Code Playgroud)


小智 7

当lambda表达式从其封闭空间使用指定的局部变量时,存在一个重要的限制.lambda表达式只能使用其值不会更改的局部变量.该限制被称为" 变量捕获 ",其被描述为; lambda表达式捕获值,而不是变量.
lambda表达式可能使用的局部变量称为" 有效最终 ".
有效的最终变量是在首次分配后其值不变的变量.没有必要将这样的变量显式地声明为final,尽管这样做不会是错误.
让我们看一个例子,我们有一个局部变量i,它用值7初始化,在lambda表达式中,我们试图通过给i赋值来改变该值.这将导致编译器错误 - " 我在封闭范围中定义的局部变量必须是最终的或有效的最终 "

@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;
        };
    }   
}
Run Code Online (Sandbox Code Playgroud)