【问题标题】:Accessing outer class final local variable from anonymuos inner class从匿名内部类访问外部类最终局部变量
【发布时间】:2015-05-06 11:55:21
【问题描述】:

Java 匿名内部类允许无缝访问在外部对象的方法堆栈上声明的变量,只要它们声明为 final

但是,如果我在内部类中声明了一个同名的变量怎么办?有没有办法显式引用外部变量?

public class Outer {
    public static void main(String[] args) throws Throwable {
        new Outer().call();
    }

    private void call() throws Throwable {
        final String str = "I'm the outer String!";
        SwingUtilities.invokeAndWait(new Runnable() {

            @Override
            public void run() {
                String str = "I'm the inner String!";

                // This prints the inner String
                System.out.print(str);

                // So, can I have explicitly access to the outer String?
            }
        });
    }
}

顺便说一句,这与 this 问题不同,因为它涉及 本地堆栈 变量。

【问题讨论】:

  • 如果您使用不同的名称作为字符串引用,它是否有效?
  • 即使有一些方法可以让它工作,代码也会造成不必要的混乱和不可读。简单实用的解决方案(虽然不是您的问题的答案)是使用不同的标识符。

标签: java variables anonymous-class


【解决方案1】:

您可以在 run 函数中使用 str 外部变量,直到声明具有相同名称的新局部变量为止。 此时,您的新变量会影响另一个变量。

如果你想同时使用过去声明第二个 str,你必须将它存储在其他对象中。

您的 run 方法在当前线程堆栈中获得一个私有 堆栈帧。 新变量只是被添加到该堆栈中。执行run后,栈帧被移除。

你想要的是在它的范围之外访问方法参数(在新的 str 添加到堆栈之后)。

但是,外部 str 的堆栈帧只能访问 java 调试架构。 将此视为安全问题。

【讨论】:

    【解决方案2】:

    这很有趣。 Outer.this.str 肯定不起作用,因为 str 是方法内部的局部变量,而不是类中的局部变量。

    在我的测试中,我使用了 Java 8 lambda 表示法:

    final String str = "I'm the outer String!";
    SwingUtilities.invokeAndWait(() -> {
        String str = "I'm the inner String!"; // error: str is already defined in this scope
    
        // This prints the inner String
        System.out.print(str);
    
        // So, can I have explicitly access to the outer String?
        }
    );
    

    这里编译器实际上输出了一个错误,说变量str已经在这个作用域中定义了。在您的示例中,尽管似乎没有办法访问外部变量。

    您刚刚使用匿名类中的变量遮蔽了str(我认为这在 Java 中是不可能的……但我想只要它是一个实际的匿名类,它就可以工作)。

    【讨论】:

      【解决方案3】:

      如果你想访问类变量,那么你可以调用

      Outer.this.str
      

      但它不是类变量,它是局部变量,在call 方法中声明。并且 run 方法内部的 str 声明隐藏(隐藏)外部 str 变量。

      如果你想避免代码中难以捕捉的错误,你应该避免使用变量的这种方法。

      结论:你不能从你的run方法中引用外部局部变量str,因为有一个同名的变量, 在您的 run 方法中声明,它正在遮蔽外部本地 str 变量。

      【讨论】:

        【解决方案4】:

        如果被内部类中的变量隐藏,您可以通过执行以下操作来引用外部类中的变量:

        System.out.println(Outer.this.str);
        

        更新:我认为一旦隐藏了变量,您就不能在方法中引用它。通过重复名称来避免隐藏变量的原因之一。其他;它变得令人困惑。

        【讨论】:

        • 它是方法中的局部变量
        • 如前所述,这是正确的,但我现在看到它没有回答问题。
        【解决方案5】:

        如果您从 Inner 引用的字段是 Outer.class 中的字段,它会起作用。如果是localVariable,则使用inner-Class的localVariable。

        public class Outer {
            private String shadowed = "Outer";
        
            public static void main(String[] args) throws Throwable {
                new Outer().call();
            }
        
            private void call() throws Throwable {
                final String str = "I'm the outer String!";
                shadowed = "OuterString";
        
                SwingUtilities.invokeAndWait(new Runnable() {
                    private String shadowed = "Outer";
        
                    @Override
                    public void run() {
                        String inner = "I'm the inner String!";
                        shadowed = "InnerString";
        
                        // different reference identifier
                        System.out.println(inner);
                        System.out.println(str);
        
                        // same reference identifier
                        System.out.println(shadowed);
                        System.out.println(Outer.this.shadowed);
                    }
                });
            }
        }
        

        【讨论】: