使用 java final 关键字作弊

cheat with java final keyword

我正在写一些 java 代码,有一次我被一件事阻止了。

final String action = "action1";
final Button otherAction = (findById....);
otherAction.setOnClickListener(new View.OnClickListener() {
    @Override
        public void onClick(View v) {
            if (action.equals("action1")) {
                action = "action2";
            } else if (action.equals("action2")) {
                action = "action3";
            } else if (action.equals("action3")) {
                action = "action1";
            }
        }
    });

显然这段代码不起作用,因为我无法为 action 分配新值,因为它是最终变量,因此只能初始化一次。
要从 onClickListener 范围内访问变量,您必须将其声明为 final。

所以我解决这个问题的方法是:

final Button otherAction = (findById....);
otherAction.setOnClickListener(new View.OnClickListener() {
    @Override
        public void onClick(View v) {
            if (t.action.equals("action1")) {
                t.action = "action2";
            } else if (t.action.equals("action2")) {
                t.action = "action3";
            } else if (t.action.equals("action3")) {
                t.action = "action1";
            }
        }
    });

Class t {
    public static String action = "action1";
}

我的问题是:为什么这有效?

字段不必是 final 就可以从匿名 classes 访问,只是方法变量。

这是因为字段(其实体或class)存在直到不再需要它(由 GC 确定),所以总有一个地方可以分配。

但是,如果您在某处传递匿名 class 实例,当包含方法退出时它可能仍然存在。想象一下在遥远的未来某个时候调用此侦听器的其他操作。但是没有地方可以再分配这个变量,因此它应该是最终的并在任何 class 实例化时复制。

据我了解,第一个代码块没有 work.The "work" 这里的意思是:编译错误。

第二个之所以有效,是因为它遵循 Java 语言规范。对非局部参数、形式参数没有限制。t.action是静态变量。

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be declared final.

Any local variable used but not declared in an inner class must be definitely assigned (§16) before the body of the inner class.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.1.3

需要注意的是,在Java 8之前。Java 8引入了以下概念:effective final which the local variables used for inner class like above don't need正式宣布为最终的。有关更多详细信息,可以在以下位置查看: https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be **effectively** final (§4.12.4), or a compile-time error occurs where the use is attempted.

在此处查看有关 effective-final 的更多解释: Difference between final and effectively final

关于第二个代码块为什么有效,你可以在这里阅读: Why are only final variables accessible in anonymous class?

这是我见过的关于这个问题最合理的答案:)