【问题标题】:Can instance variables be effectively final / Are only local variables effectively final in Java?实例变量可以有效地最终/Java中只有局部变量有效地最终吗?
【发布时间】:2021-02-24 19:42:35
【问题描述】:

我想在 Java 9 中使用 try-with-resources 增强功能,方法是将引用变量放在 try with resources 中,而不是整个变量声明。我也知道要做到这一点,我必须遵守规则:Variable used as a try-with-resources resource should be final or effectively final。首先我将尝试使用本地变量,然后使用实例变量。

  1. 局部变量:

-我将变量设为final,它遵循给定的规则并编译得很好:

public static void main (String[] args) throws IOException{
        final FileWriter fw = new FileWriter ("test.txt");
        try(fw) {
            //some code
        }
    }

-如果我还删除了 final 关键字,它将再次编译,因为 fw 被认为是实际上是最终的 - 仅初始化一次且从不变异的变量。

public static void main (String[] args) throws IOException{
     FileWriter fw = new FileWriter ("test.txt");
    try(fw) {
        //some code
    }
}
  1. 实例变量:

但是同样的模式也适用于实例变量吗?让我们试试吧。

-首先让我们创建实例变量final,它再次遵循规则并且编译良好:

public class Test {
  final FileWriter fw = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fw ) {
            //some code
        }
    }
}

-如果我删除 final 关键字,它应该再次编译,不是吗?因为我没有在任何地方改变fw,而是只初始化一次 - 应该是有效的最终。不幸的是,这不起作用:

public class Test {
   FileWriter fileWriter = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fileWriter) {
            //some code
        }
    }
}

它给我的信息是:用作 try-with-resources 资源的变量应该是最终的或实际上是最终的。所以在这一切之后,我回到我的第一个问题。实例变量是否可以实际上是最终的,或者该术语仅用于局部变量?正如我刚刚展示的那样,我从不改变变量(应该将其视为实际上是最终的),但编译器从不威胁它。

【问题讨论】:

  • 只有局部变量才能有效地为final。
  • 我怀疑这与可能访问它的子类有关。如果你把它设为私有,它还会抱怨吗?
  • @Gus 是的,它仍然在抱怨。
  • 在 Java 中,没有访问修饰符意味着默认访问,这意味着该包中的任何类都可以访问该数据成员。因此,它不是有效的 final 仅仅因为它没有从声明类本身修改。它可以在类外修改。它基本上对包是 PUBLIC。
  • @hfontanez 完全有道理。所以我的回答是,正如 Andreas 所说,只有局部变量才是有效的最终变量?

标签: java variables final try-with-resources effectively-final


【解决方案1】:

Java Language Specification4.12.4. final Variables 部分明确规定只有局部变量和参数才能有效地为 final

某些未声明 final 的变量被视为实际上是最终的

  • 如果满足以下所有条件,则声明符具有初始化器 (§14.4.2) 的局部变量实际上是最终的

    • 没有声明final

    • 它永远不会出现在赋值表达式 (§15.26) 的左侧。 (请注意,包含初始化程序的局部变量声明符不是赋值表达式。)

    • 它永远不会作为前缀或后缀递增或递减运算符(§15.14§15.15)的操作数出现。

  • 如果满足以下所有条件,则声明符缺少初始化器的局部变量实际上是最终的

    • 没有声明final

    • 当它出现在赋值表达式的左侧时,它肯定是未赋值的,并且在赋值之前也不是肯定赋值的;也就是说,在赋值表达式的右侧(§16 (Definite Assignment))之后,它肯定是未赋值的,也不是肯定赋值的。

    • 它永远不会作为前缀或后缀递增或递减运算符的操作数出现。

  • 处理方法、构造函数、lambda 或异常参数§8.4.1§8.8.1§9.4§15.27.1§14.20),目的是确定是否它实际上是最终的,作为一个局部变量,它的声明器有一个初始化器。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    相关资源
    最近更新 更多