为什么匿名类可以访问封闭类的非final类成员

时间:2015-06-11 06:46:11

标签: java closures final anonymous-class

我们知道只能在匿名类中访问最终的局部变量,这里有一个很好的理由:Why are only final variables accessible in anonymous class?

但是,我发现如果变量是封闭类的成员字段,匿名类仍然可以访问非final变量:How can I access enclosing class instance variables from inside the anonymous class?

我很困惑。我们确保只能在匿名类中访问最终的局部变量,因为我们不希望变量在匿名类和本地函数之间不同步。如果我们尝试访问匿名类中的非最终封闭类成员,则同样的理由应该适用于该情况。

为什么不关注它?

4 个答案:

答案 0 :(得分:7)

对于局部变量,变量的副本是匿名类实例接收的内容。出于这个原因,必须先将局部变量设为final,然后才能在匿名类中使用它,以便以后可能不会更改其值。

如果是封闭类的成员字段,则 没有副本 。相反,匿名类获取对封闭类的引用,从而访问外部类的任何/所有成员字段和方法。因此,即使字段的值发生更改,更改也会反映在匿名类中,因为它是相同的引用。

  

我很困惑。我们确保只有最终的局部变量   在匿名类中访问因为我们不想要变量   在匿名类和本地函数之间应该是不同步的。该   如果我们尝试访问非决赛,同样的理由应适用于该案例   将类成员包含在匿名类中。

如你所见,事实并非如此。复制仅针对局部变量,而不针对封闭类的成员字段。 原因当然是匿名类持有对封闭类的隐式引用,并且通过该引用它可以访问任何/所有成员字段&外类的方法。

引用以下链接:

  

成员变量在封闭对象的生命周期内存在,因此内部类实例可以引用它。但是,局部变量仅在方法调用期间存在,并且由编译器以不同方式处理,因为它的隐式副本是作为内部类的成员生成的。如果不声明局部变量fi​​nal,可以改变它,导致细微的错误,因为内部类仍然引用该变量的原始值。

<强>参考文献:

1。 Why a non-final “local” variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can?

答案 1 :(得分:2)

非静态/内部类具有对封闭实例的引用。所以他们可以隐含地引用实例变量和方法。

如果它是一个参数,即使是封闭类也不知道它的任何内容,因为它只能从定义了这个变量的方法中访问。

和Y.S.一样已经指出:

  

对于局部变量,变量的副本是匿名类实例获取的内容

答案 2 :(得分:1)

反过来说,只要方法调用,匿名实例外的局部变量(x)就会存在。它的对象引用存储在调用堆栈中; x ==包含对象引用的堆栈上的地址。匿名实例中的x副本,因为它的生命周期不同,更长甚至更短。

由于现在有两个变量,因此决定不允许赋值给x(奇怪地实现)并且要求变量是#34;实际上是最终的。&# 34;

访问外部成员,是因为匿名实例是一个内部类,也包含一个OuterClass.this引用。

答案 3 :(得分:1)

考虑以下示例

class InnerSuper{
    void mInner(){}
}
class Outer{
    int aOuter=10;
    InnerSuper mOuter(){
        int aLocal=3999;
        class Inner extends InnerSuper{
            int aInner=20;
            void mInner(){
                System.out.println("a Inner : "+aInner);
                System.out.println("a local : "+aLocal);
            }
        }
        Inner iob=new Inner(); 
        return iob;
    }
}

class Demo{
    public static void main(String args[]){
        Outer ob=new Outer();
        InnerSuper iob=ob.mOuter(); 
        iob.mInner();
    }
}

这不会在Java 1.8或更高版本中生成任何错误。但在以前的版本中,这会产生一个错误,要求您明确声明在内部类中访问的本地变量为 final 。 因为编译器所做的是它将保留内部类访问的局部变量的副本,以便即使方法/块结束且局部变量超出范围,副本也将存在。它要求我们声明 final ,因为如果变量在声明本地内部类匿名类之后在程序中稍后动态更改其值,编译器创建的副本不会更改为新值,并且可能会因为不生成预期输出而导致内部类中的问题。因此,它建议我们明确宣布它。

但是在Java 1.8中它不会产生错误,因为编译器会隐式地声明最终访问的本地变量。 它在Java Docs中说明如下

  

匿名类无法访问其封闭的局部变量   未被宣布为最终或有效最终的范围。

让我解释一下有效决赛的含义。请考虑以上程序的以下更改版本

class Outer{
    int aOuter=10;
    InnerSuper mOuter(){
        int aLocal=3999;
        class Inner extends InnerSuper{
            int aInner=20;
            void mInner(){
                System.out.println("a Inner : "+aInner);
                System.out.println("a local : "+aLocal);
            }
        }
        aLocal=4000;
        Inner iob=new Inner(); 
        return iob;
    }
}

即使在Java 1.8中,这也会产生错误。这是因为aLocal是在程序中动态分配的。这意味着编译器不能将变量视为有效最终。从我所理解的情况来看,编译器将未动态更改的变量声明为final。这被称为变量有效最终

因此,建议您将Local内部类或匿名类访问的局部变量声明为final,以避免任何错误。