当前位置: 首页 > 知识库问答 >
问题:

为什么Java中的局部变量不被认为是“有效的最终”,即使之后没有任何修改?

姜俊友
2023-03-14

在一种方法中,我有:

int x = 0
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}

if (x != 0) {
    doLater(() -> showErrorMessage(x)); // compile error here
}

// no more reference to 'x' here

我不明白为什么它会产生编译错误。错误表明<code>x<code>不是final或实际上不是final,因此无法从lambda主体访问它。在<code>doLater

我猜这个问题的答案是因为< code>x没有资格被称为effectively-final变量。但是,我想知道是什么原因。

难道编译器不能创建一个临时的最终变量,有效地使代码像:

if (x != 0) {
    final int final_x = x;
    doLater(() -> showErrorMessage(final_x));
}

一切都照旧吗?

共有3个答案

耿珂
2023-03-14

在短版本中,如果变量只被赋值一次,则无论执行哪个代码路径,它都是有效的最终变量。

长版,引用Java语言规范4.12.4。最终变量(强调我的):

某些未声明为< code>final的变量实际上被视为最终变量:

  • 如果以下所有条件都为真,则其声明符具有初始值设定项 (§14.4.2) 的局部变量实际上是最终变量:
    • 它未被宣布为最终结果
    • 它永远不会在赋值表达式 (§15.26) 中作为左侧出现。(请注意,包含初始值设定项的局部变量声明符不是赋值表达式。
    • 它从不作为前缀或后缀递增或递减运算符的操作数出现(§15.14, §15.15)。

    现在,您可以通过删除初始化程序使其有效地最终化,因为它继续:

    • 如果以下所有内容都为真,则声明符缺少初始化器的局部变量实际上是最终的:
      • 它没有声明Final
      • 每当它在赋值表达式中作为左侧出现时,它在赋值之前肯定是un在赋值,而不是绝对赋值;也就是说,它在赋值表达式的右侧之后肯定是un赋值,而不是绝对赋值(§16(明确赋值))。
      • 它永远不会作为前缀或后缀递增或递减运算符的操作数出现。

韩嘉祯
2023-03-14

如果您的<code>x</code>变量被初始化一次,并且在任何情况下都不会再次更改,那么它将是有效的最终变量。如果你只有:

int x = 0;
doLater(() -> showErrorMessage(x));

然后它就会编译。

但是,添加可能会改变变量值的条件

int x = 0;
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}

使得变量不是有效的最终变量,因此编译错误增加。

此外,由于您实现的这种指针方法不起作用,因此您可以将代码重构为一个简单的if-else语句:

if (isA()) {
    doLater(() -> showErrorMessage(1));
} else if (isB()) {
    doLater(() -> showErrorMessage(2));
}

并完全消除<code>x</code>。

毋承基
2023-03-14

有效的最终意味着它本来可以成为最终的,即它永远不会改变。这意味着实际上,变量可能是最终变量。

问题是它没有记录您上次更改它的时间,而是记录您是否更改过它。将您的if语句更改为

int x;
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}  else {
    x = 0;
}

int x = isA() ? 1 : 
        isB() ? 2 : 0;
 类似资料:
  • 问题内容: 在C / C ++中,我们使用静态局部变量来维护方法的状态。但是,为什么Java不支持它呢? 是的,我可以为此使用一个静态字段。但是创建一个仅维护一个方法状态的字段有点奇怪吗? 问题答案: 您已经找到了唯一的解决方案。 Java放弃了C ++的许多复杂性,这就是其中之一。 作用于函数的静态变量并发地对您造成麻烦(例如,正是由于这个原因,strtok是与pthread一起使用的著名的讨厌

  • 问题内容: 这个问题已经在这里有了答案 : 为什么在匿名类中只能访问最终变量? (15个答案) 为什么实例变量“忽略Lambda表达式中使用的变量必须是最终变量或实际上是最终变量”警告[重复] (2个答案) Lambdas:局部变量不需要最终变量,实例变量不需要 (10个答案) 2年前关闭。 当我编写此代码时,我收到一个编译时错误,该错误是: “ lambda中的变量必须是final或有效的fin

  • 问题内容: 6年前关闭。 有关匿名类的文档说明 匿名类无法在其封闭范围内访问未声明为final或有效地为final的局部变量。 我不明白变数为“有效的最终”是什么意思。有人可以提供一个示例来帮助我理解这意味着什么吗? 问题答案: 有效地最终意味着它在获得初始值之后永远不会改变。 一个简单的例子: 在这里,并没有声明为final,但是由于它从未更改,因此实际上被认为是 final 。 从Java 8

  • 问题内容: 我是一位Java程序员,对公司领域来说是新手。最近,我已经使用Groovy和Java 开发了一个应用程序。我编写的所有代码都使用了大量的静态变量。高级技术人员要求我减少使用的静电数量。我已经在谷歌上搜索了相同的内容,并且发现许多程序员都反对使用静态变量。 我发现静态变量更易于使用。而且我认为它们也是有效的(如果我错了,请纠正我),因为如果我必须对一个类中的一个函数进行10,000次调用

  • 我读到了这个关于不可变对象的问题,留下了一个关于不可变对象和final字段的问题: 为什么我们需要不可变类中的实例变量成为最终变量? 例如,考虑这个不可变类: 如果在上面的代码中没有设置方法,并且实例变量只在构造函数中设置,那么为什么需要将实例变量声明为final?