【问题标题】:Why it says that "Cannot refer to a non-final variable i inside an inner class defined in a different method"? [duplicate]为什么它说“不能在以不同方法定义的内部类中引用非最终变量 i”? [复制]
【发布时间】:2011-10-21 07:52:47
【问题描述】:

我有按钮单击侦听器,在 onCreate() 方法中我有一个局部变量,例如

 onCreate() {

 super.onCreate();

 int i = 10;

 Button button = (Button)findViewById(R.id.button);

 button.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            i++;
        }   
    });

为什么 java 要求让我成为 final ?

【问题讨论】:

    标签: java android inner-classes final local-variables


    【解决方案1】:

    当 onCreate() 方法返回时,您的局部变量将从堆栈中清除,因此它们将不再存在。但是匿名类对象 new View.OnClickListener() 引用了这些变量。当然这是错误的行为,所以 java 不允许你这样做。

    在它是 final 之后,它变成了一个常数。所以它存储在堆中,可以安全地在匿名类中使用。

    【讨论】:

    • 好的。你提到i是本地的原因,它会在onCreate()完成后被销毁,所以onClickListener()将无法获取i的值。但是如果 i 是最终的,那么我认为,在 onCreate() 销毁之后它也会销毁。那么在 onClickListener 中它是如何使用的呢?
    • 编译器只会在编译时将匿名类中 i 的使用替换为常量的值,访问不存在的变量不会有问题。
    • 是final变量的特例吗?
    • 不,ofc。正如您应该知道的,java 中的变量只是对对象的引用。如果变量不是最终的,它的引用可能被设置为空。但是当它是最终的时候,你没有能力改变它。所以这个变量在方法返回后仍然存在。
    【解决方案2】:

    您的匿名内部类通过获取局部变量的副本来引用其封闭范围 - 如果您想更改匿名内部类中 int 的值,您需要做一些hackery:

    final int[] arr = new int[1];
    arr[0] = 10;
    Button button = (Button)findViewById(R.id.button);
    
    button.setOnClickListener(new View.OnClickListener() {
      arr[0]++;
    }
    

    【讨论】:

    • 我喜欢这种方法...也许不清楚,但“hackery”。
    • 另一个黑客:class Temp { public int i = 10; } final Temp temp = new Temp(),然后使用temp.i
    • 巧妙!谢谢!
    • 我更喜欢org.apache.commons.lang3.mutable.MutableInt final MutableInt = new MutableInt(10)
    【解决方案3】:

    因为您在匿名内部类中访问它。你不能那样做。您可以将其设为final,然后从匿名内部类中读取它,但您不能对其进行递增。

    选项:

    • 改为使其成为外部类的实例变量
    • 改为匿名内部类的实例变量
    • 使用包装器 - 例如一个单元素数组,AtomicInteger 或类似的东西

    除非我需要从其他任何地方联系i,否则我可能会倾向于第二种选择。老实说,我认为第三个选项有点令人讨厌。

    【讨论】:

    • 虽然其他人只给出了理由,但只有你在给出解决方案(提出问题的人可能不会想到:))....因此赞成
    【解决方案4】:

    创建变量final 是必要的,因为在底层,像这样的匿名内部类只是syntactic sugar,它编译为嵌套类包含方法的范围之外。

    这意味着内部类无法访问在方法内部声明的任何变量,因此编译器采用了另一个技巧 - 并复制类的隐藏构造函数中的值。为避免程序员混淆此副本未更新以匹配方法中变量的更改,它必须是 final 以确保没有此类更改。

    由于您的目标是有一个递增的整数,并且只有引用必须是最终的(对象本身不必是不可变的),您可以声明一个 final AtomicInteger i,然后从回调中根据需要递增它。

    【讨论】:

      【解决方案5】:

      由于您需要增加 i 变量,因此您无法将其设为最终变量。您可以改为将其设为类成员。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-20
        • 2014-12-09
        • 1970-01-01
        • 2012-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多