为什么实例初始化器需要 * final * Java中的外部变量



如果第二个示例可以访问外部变量,则会违反哪些语义?

class A {
    void f() {
        int outer = 1;
        // Access non-final outer variable through helper method
        new A() {
            int inner;
            void init(int inner) {
                this.inner = inner;
            }
        }.init(outer); // OK
        // Access non-final outer variable through instance initializer
        new A() {
            {
                // int inner = outer; // Does not compile
            }
        };
        outer = 2;
    }
}

它与实例initializers 无关,而是与您捕获 的事实。如果您捕获助手方法中的外部局部变量,则同样行不通:

new A() {
    int inner;
    void init() {
        this.inner = outer;
    }
}.init();

当您 capture 局部或匿名类中的外部局部变量时,外部局部变量必须为 final(在Java 7或之前)或有效地final (在Java 8 )。您的变量outer不是final,并且(在最近的编辑之后)不是有效的final (这意味着如果将其称为final),因为您以后分配给它。

由于Java实施捕获的方式,捕获的本地变量必须为final或有效的final。在Java中,当创建本地或匿名类的对象时,将其捕获的任何局部变量分配(就像由=)分配到对象内部的单独独立副本中(因为对象可能比创建其创建的本地范围更高)。最初的本地范围和捕获它的对象之间并未"共享"变量状态,即使它们具有相同的名称,并且只声明了一个变量。因此,如果您可以将变量分配给变量的一个副本,则不会反映在其他副本中,这将是不一致的。为了防止这种情况,它们通过要求有效的final来防止您分配到变量的任何版本。

最新更新