如果非最终字段的值可以更改,那么如何在非匿名字段类中使用非最终字段? [英] How can non-final fields be used in a anonymous class class if their value can change?

查看:111
本文介绍了如果非最终字段的值可以更改,那么如何在非匿名字段类中使用非最终字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我之前问过这个问题,但没有得到适当的答案.

I asked this question before but I didn't get an appropriate answer.

如果非final字段的值可以更改,那么如何在非匿名字段类中使用它们?

class Foo{
    private int i;
    void bar(){
        i = 10
        Runnable runnable = new Runnable (){
            public void run (){
                System.out.println(i); //works fine
            }//end method run
        }//end Runnable
    }//end method bar
}//end class Foo 

如果在匿名类中使用的局部变量必须为final,以使编译器可以在匿名类代码中内联其值,如下所示:

If the local variables which are used inside an anonymous class must be final to enable the compiler inlining their values inside the anonymous class code like that:

之前:

public class Access1 {
  public void f() {
    final int i = 3;
    Runnable runnable = new Runnable() {
        public void run() {
            System.out.println(i);
        }//end method run
    };//end anonymous class
  }//end method f
}//end class Access1

之后:

public class Access1 {
    public Access1() {}//end constructor

    public void f() {
        Access1$1 access1$1 = new Access1$1(this);
    }//end method f
}//end class Access1

还有

class Access1$1 implements Runnable {
    Access1$1(Access1 access1) {
        this$0 = access1;
    }//end constructor

    public void run() {
        System.out.println(3);
    }//end method run
    private final Access1 this$0;
}//end class Access1$1

然后编译器如何内联非最终字段的值?

推荐答案

方法调用的局部变量(内部类必须可访问final)与实例的私有数据成员之间存在很大差异.

There's a big difference between a method call's local variable (which must be final to be accessible to an inner class), and an instance's private data members.

内部类可以访问包含的实例,以及该实例的所有成员,无论是否为final.不需要最后确定它们,因为通过(在您的情况下)Foo.this引用了它们.因此,在访问您的i成员时,内部类实际上是在访问Foo.this.i,只是如果引用没有它,就可以隐含Foo.this(如this).

The inner class has access to the containing instance, and to all of the members of that instance, final or not. There's no need for them to be final, because they're referenced through (in your case) Foo.this. So when accessing your i member, the inner class is really accessing Foo.this.i, it's just that Foo.this (like this) can be implied if a reference is unambiguous without it.

但是匿名类的代码无法以这种方式访问​​局部变量,因为它们(当然)不是包含类的实例成员.因此,相反,编译器做了一件非常有趣的事情:它为每个final局部变量创建 anonymous 类的实例成员,并且在创建匿名类的实例时,使用以下方法初始化这些成员:局部变量的值.

But the anonymous class's code can't access local variables that way, because they aren't (of course) instance members of the containing class. So instead, the compiler does a very funny thing: It creates an instance member of the anonymous class for each final local variable, and when creating the instance of the anonymous class, it initializes those members with the values of the local variables.

让我们看一下它

public class InnerEx {
    public static final void main(String[] args) {
        new InnerEx().test("hi");
    }

    private void test(String arg) {
        final String localVar = arg;

        Runnable r = new Runnable() {
            public void run() {
                System.out.println(localVar);
            }
        };
        r.run();
    }
}

编译后,我们得到InnerEx.classInnerEx$1.class.如果我们反编译InnerEx$1.class,我们会看到:

When compiled, we get InnerEx.class and InnerEx$1.class. If we decompile InnerEx$1.class, we see this:

class InnerEx$1 implements java.lang.Runnable {
  final java.lang.String val$localVar;

  final InnerEx this$0;

  InnerEx$1(InnerEx, java.lang.String);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #1                  // Field this$0:LInnerEx;
       5: aload_0       
       6: aload_2       
       7: putfield      #2                  // Field val$localVar:Ljava/lang/String;
      10: aload_0       
      11: invokespecial #3                  // Method java/lang/Object."<init>":()V
      14: return        

  public void run();
    Code:
       0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0       
       4: getfield      #2                  // Field val$localVar:Ljava/lang/String;
       7: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: return        
}

请注意名为val$localVar的实例成员,该实例成员是为代表对InnerEx#test的调用中的局部变量而创建的实例成员.

Note the instance member called val$localVar, which is the instance member created to stand in for the local variable in the call to InnerEx#test.

这篇关于如果非最终字段的值可以更改,那么如何在非匿名字段类中使用非最终字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆