【问题标题】:public anonymous inner class in JavaJava中的公共匿名内部类
【发布时间】:2012-03-23 15:28:15
【问题描述】:

默认情况下匿名内部类是私有的吗?我可以将它们公开吗?

我需要通过反射来访问方法。

【问题讨论】:

  • 如果它是一个匿名内部类,编译器如何知道在哪里调用这些方法?为什么不只是创建一个公共类并在需要的地方使用它而不是使用匿名类?

标签: java public anonymous-inner-class


【解决方案1】:

匿名内部类之所以匿名是有原因的:它们并不意味着可以直接从外部世界访问,只能通过引用变量/方法参数来访问。 (出于同样的原因,它们也是私有的。)

我猜你可能尝试使用编译器生成的名称(例如OuterClass$1)通过反射访问这样的类,但是这是特定于实现的,并且可能会在您添加另一个匿名内部时改变类到相同的外部类,或在下一个 JVM 版本中。所以这样的解决方案会很脆弱。

您为什么要这样做?如果您解释您的实际问题,我们或许可以提供更好的替代方案。

【讨论】:

  • 我不同意。非匿名内部类和匿名内部类之间的唯一区别是后者没有名称。这样做的原因是简洁,而不是安全性。所以匿名类不能公开的原因只是没有提出语法。恕我直言。
  • +1 如果您有实例,则不必使用编译器生成的类名(这是匿名内部类所固有的)。
  • @XaviLópez,确实如此。如果你有实例,为什么需要反射?
  • 我们绝对应该对这件事有一些背景来给出合理的答案
  • @PéterTörök - 您将使用反射来访问匿名类中声明的方法。
【解决方案2】:

您可以通过反射访问匿名内部类的方法。见getDeclaredMethods()。 请记住在Method 上致电setAccessible(true) 以避免IllegalAccessException

Object testObject = new Object() {
    private void testMe() { 
        System.out.println("testme");
    }
};
Method m = testObject.getClass().getDeclaredMethod("testMe");
m.setAccessible(true);
m.invoke(testObject); // prints out "testme"

另请注意,如果有 SecurityManager,这是不可能的,请参阅 What is the security risk of object reflection?

警告:请注意匿名内部类是一种一次性类定义。一旦使用,您将不再需要它们。就像@PéterTörök 所说,如果没有更多关于您的问题的上下文,很难判断,但是,如果您可以控制该类,最好将该类去匿名化(创建一个私有内部类,甚至是公共的),并将该方法公开给需要它的类。

【讨论】:

    【解决方案3】:

    匿名内部类默认是私有的。要与反射一起使用,您可以在这里查看 - Java reflection: How can I retrieve anonymous inner classes?

    【讨论】:

    • 匿名内部类具有默认(包私有)访问权限,而不是私有的。
    【解决方案4】:

    匿名内部类具有包私有(默认)访问权限。在 Java 6 中,如果在静态上下文中声明它们是最终的,但在其他上下文中不是最终的。 (我相信,但尚未测试,这在 Java 7 中已经改变,因此它们始终是最终的;请参阅 Section 15.9.5 of the Java Language Specification。)

    比如这个类有四个匿名内部类:

    public class InnerTest {
        public Runnable foo1 = new Runnable() {
            public void run() {foo1();}
            void foo1() {}
        };
        private Runnable foo2 = new Runnable() {
            public void run() {foo2();}
            void foo2() {}
        };
        public static Runnable foo3 = new Runnable() {
            public void run() {foo3();}
            void foo3() {}
        };
        private static Runnable foo4 = new Runnable() {
            public void run() {foo4();}
            void foo4() {}
        };
    }
    

    当使用 javac(版本 1.6.0_26)编译时,它会生成四个匿名内部类。使用javap -c 反编译显示:

    • InnerTest$1 (foo1) — 包私有
    • InnerTest$2 (foo2) — 包私有
    • InnerTest$3 (foo3) — 包私有和最终
    • InnerTest$4 (foo4) — 包私有和最终

    请注意,匿名内部类实例被分配到的变量的访问是无关紧要的。

    【讨论】: