【问题标题】:Are static anonymous classes definitely wrong in Java?Java中的静态匿名类肯定是错误的吗?
【发布时间】:2009-08-07 08:56:45
【问题描述】:

我在其他地方读到静态匿名类没有意义——所有匿名类都应该绑定到封闭类型的实例。但是编译器让你去做。这是一个例子:

class Test {

    /*
     * What's the difference at between
     * Test.likeThis and Test.likeThat?        
     */

    // This is obviously okay:
    private static final class LikeThat {
        @Override
        public String toString() { return "hello!"; }       
    }
    public static Object likeThat = new LikeThat();

    // What about this - is it really any different? 
    public static Object likeThis = new Object() {
        @Override
        public String toString() { return "hello!"; }
    };
}

这是怎么回事?

【问题讨论】:

  • 呃,它不是绑定到一个实例,因为你实例化了它?
  • 不,我的意思是封闭类型的一个实例。我将修改问题以使这一点更清楚。

标签: java


【解决方案1】:

来自the Java Language Specification, section 8.1.3

内部类的实例I 其声明出现在静态上下文中没有词法封闭的实例。但是,如果 I 立即在静态方法或静态初始化程序中声明,则 I 确实有一个 封闭块,它是词法上最里面的块语句附上I的声明。

您的匿名类(likeThis 是其中的一个实例)出现在静态上下文中,因此它与封闭实例无关。但是,它似乎可以引用其封闭块的最终变量(参见第 8.1.3 节的其余部分,他们给出了一个示例)。

顺便说一句,您的措辞有点欺骗性,您实际上指的是匿名类的静态实例(它是静态的实例,而不是类)。

【讨论】:

    【解决方案2】:

    我认为静态匿名类没有任何问题

    【讨论】:

      【解决方案3】:

      就像任何语言中的任何事情一样,您应该考虑为什么您正在这样做。如果你有很多这样的例子,那么我会质疑设计决策,但这并不一定意味着它是一种永远不应该遵循的模式。

      当然,请始终考虑类的可测试性以及是否可以在需要时提供测试替身

      【讨论】:

      • +1 - 同意,如果您看到大量静态数据,您的代码库将会很脆弱,并且您并没有真正利用实例概念带来的 OO 的强大功能。
      【解决方案4】:

      我认为他们没有任何意义。如果您不需要引用封闭对象,那么最好将其保留为静态。稍后它可以轻松地在单独的类中进化。

      广泛传播的枚举习语(Java 5 之前)使用与枚举类的匿名静态继承者类似的方法。也许,对于这种情况,现在最好坚持 Java 5 enum

      如果您能够为匿名静态类找到足够的实际应用程序 - 为什么不使用它们?

      【讨论】:

      • +1 指出您以后可以随时将其设为命名类。
      【解决方案5】:

      我一直这样做。对于实用程序接口的特殊情况实现特别方便,例如:

      /** A holder for {@link Thing}s. */
      public interface ThingsHolder {
      
          /** A {@link ThingsHolder} with nothing in it. */
          public static final ThingsHolder EMPTY_HOLDER = new ThingsHolder() {
              @Override
              public Iterable<Thing> getThings() {
                  return Collections.emptySet();
              }
          };
      
          /** Provides some things. */
          Iterable<Thing> getThings();
      }
      

      您可以创建一个名为 EmptyHolder 的私有静态内部类,并且在某些情况下可能会使代码更具可读性,但您没有理由必须这样做。

      【讨论】:

        【解决方案6】:

        根据引用 JLS 的 this answer,匿名类从不 static,但在“静态上下文”中创建时,它们没有“封闭实例”。

        也就是说,

        • 如果您尝试引用 Test.this,它们会在编译时给出相同的错误(不能从静态上下文引用非静态变量 this
        • 在运行时,Class 对象之间唯一明显的区别(除了名称)是Test$1 是一个“匿名类”而Test$LikeThat 是一个“成员类”。他们都有一个封闭类;它们都没有封闭的构造函数或方法。 (我只检查了看起来可能的方法;可能还有其他差异。)
          • 编辑: 根据getModifiers(),Test$1 是staticTest$LikeThatstatic final!根据语言规范,Test$1 实际上应该是final。嗯...
        • 根据javap -c -verbose -s -private -l

          • Test$1 指定了一个“EnclosureMethod”(可能是Test 的静态初始化器?)
          • Test$LikeThat 在“InnerClass”下有一个额外的条目 (#12; //class Test$1) 和一个奇怪的构造函数 Test$LikeThat(Test$1)。这似乎是因为LikeThatprivate,这使得构造函数private,因此编译器生成一个“蹦床”以允许从Test 调用它。

        如果您删除 private,除了 EnclosureMethod 条目之外,它们似乎编译为大致相同的内容。

        Test$1 没有在非静态上下文中定义的 final Test this$0; 字段。

        【讨论】:

          【解决方案7】:

          对我来说似乎完全合法。由于匿名类是static,它不会引用任何封闭类,但不会因此产生不良后果。

          嗯,除了是一个隐藏的单例对象之外,这很邪恶。

          【讨论】:

          • 如果单例对象是无状态的,它们就不是邪恶的。
          【解决方案8】:

          当然不是。我总是使用静态嵌套类,除非我需要与封闭对象的隐式关联。

          在 Java 术语中,嵌套类 := 在另一个类(或接口)中声明的类。内部类是那些具有来自封闭类的关联实例的嵌套类。 (非静态成员类、本地类、匿名类)。

          隐式关联有时会阻止垃圾回收。

          【讨论】:

            【解决方案9】:

            这些可能非常方便,因为可以进行循环引用:

            class A
            { 
                public static final A _1 = new A() {
                    public A foo()
                    {
                        return _2;
                    }
                };
            
                public static final A _2 = new A() {
                    public A foo()
                    {
                        return _1;
                    }
                };
            }
            

            如果不使用匿名类,创建多个相互引用的对象可能会非常尴尬。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-20
              • 2016-07-19
              • 2017-04-11
              • 2012-11-04
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多