【问题标题】:Is it possible to make anonymous inner classes in Java static?是否可以将 Java 中的匿名内部类设为静态?
【发布时间】:2010-10-20 00:05:15
【问题描述】:

在 Java 中,嵌套类可以是static,也可以不是。如果它们是static,它们不包含对包含实例的指针的引用(它们也不再被称为内部类,它们被称为嵌套类)。

在不需要引用时忘记创建嵌套类static 会导致垃圾收集或逃逸分析出现问题。

是否也可以创建匿名内部类static?还是编译器会自动解决这个问题(它可以,因为不能有任何子类)?

例如,如果我做一个匿名比较器,我几乎不需要对外部的引用:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

【问题讨论】:

  • 忘记将内部类设为静态时,“垃圾收集或逃逸分析”有哪些问题?我以为这只是关于性能......
  • 你的内部类实例保持对其外部实例的引用,即使你不需要它。这可以防止东西被垃圾收集。想象一个创建轻量级实例的(资源密集型)工厂对象。在工厂完成工作后(例如在应用程序启动期间),它可以被处理掉,但只有在它创建的东西没有链接回来的情况下才有效。
  • 我知道,这只是一个例子,但由于它是一个重复的例子,应该提到Collections.sort(list, String.CASE_INSENSITIVE_ORDER) 从 Java 2 开始工作,阅读,因为 Collection API 存在......

标签: java syntax inner-classes


【解决方案1】:

内部类不能是静态的 - 静态嵌套类不是内部类。 The Java tutorial talks about it here.

【讨论】:

  • 我已经参考官方命名法更新了这个问题。
【解决方案2】:

不,你不能,不,编译器无法弄清楚。这就是为什么 FindBugs 总是建议将匿名内部类更改为命名为 static 的嵌套类,如果它们不使用其隐式 this 引用。

编辑: Tom Hawtin - tackline 说如果匿名类是在静态上下文中创建的(例如在 main 方法中),那么匿名类实际上是 static。但是JLSdisagrees

匿名类永远不会是abstract(第 8.1.1.1 节)。匿名类始终是内部类(第 8.1.3 节);它永远不是static(§8.1.1、§8.5.1)。匿名类始终隐含为 final(第 8.1.1.2 节)。

Roedy Green 的 Java 词汇表 says that 允许在静态上下文中使用匿名类这一事实取决于实现:

如果你想让那些维护你的代码的人感到困惑,wags 发现 javac.exe 将允许在 static 初始化代码和 static 方法中使用匿名类,即使语言规范说匿名类永远不会是 static。当然,这些匿名类无法访问对象的实例字段。我不建议这样做。 feature 可以随时拉取。

编辑 2: JLS 实际上在 §15.9.2 中更明确地涵盖了静态上下文:

C 为被实例化的类,令i 为被创建的实例。如果 C 是一个内部类,那么 i 可能有一个直接封闭的实例。 i 的直接封闭实例(第 8.1.3 节)确定如下。

  • 如果 C 是匿名类,则:
    • 如果类实例创建表达式出现在静态上下文中(第 8.1.3 节),则 i 没有直接封闭的实例。
    • 否则,i 的直接封闭实例是this

因此,静态上下文中的匿名类大致相当于 static 嵌套类,因为它不保留对封闭类的引用,即使它在技术上不是 static 类。

【讨论】:

  • FindBugs +1 - 每个 Java 开发人员都应该在他们的构建中使用它。
  • 这很不幸,因为这意味着出于性能原因,您可能希望避免这种几乎简洁的语法。
  • JLS 第三版处理静态上下文中内部类的情况。它们在 JLS 意义上不是静态的,但在问题中给出的意义上是静态的。
  • 这是一个如何依赖于实现的示例:this code 使用 javac (sun-jdk-1.7.0_10) 打印 true 并使用 Eclipse 编译器打印false
  • @MichaelMyers 我试图模拟 FindBugs,提醒我在不使用“this”引用的情况下执行匿名内部,但没有任何反应。您能否演示 FindBugs 如何像您在答案开头所说的那样提醒您?只需粘贴一些链接或其他任何内容。
【解决方案3】:

有点。在静态方法中创建的匿名内部类显然实际上是静态的,因为没有外部 this 的来源。

静态上下文中的内部类和静态嵌套类之间存在一些技术差异。如果您有兴趣,请阅读 JLS 第 3 版。

【讨论】:

【解决方案4】:

我认为这里的命名有点混乱,诚然这太愚蠢和混乱了。

不管你怎么称呼它们,这些模式(以及一些具有不同可见性的变体)都是可能的、正常的、合法的 Java:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

它们在语言规范中得到了满足(如果您真的很烦恼,请参阅第 15.9.5.1 节了解静态方法中的内容)。

但是这句话完全是错误的

javac.exe 将允许匿名 静态初始化代码中的类和 静态方法,即使 语言规范说比匿名 类永远不是静态的

我认为引用的作者将静态 keyword 与静态 context 混淆了。 (诚​​然,JLS 在这方面也有些混乱。)

老实说,上面的所有模式都很好(无论你称它们为“嵌套”、“内部”、“匿名”等等……)。真的,没有人会在 Java 的下一个版本中突然删除这个功能。老实说!

【讨论】:

  • “(诚然,JLS 在这方面也有点混乱。)”你说得对。说这取决于实现听起来很奇怪,但我不记得以前在 Java 词汇表中看到过任何明显的错误。从现在开始,我对它持保留态度。
  • 我们实际上不是在谈论任何模式。我们的意思是匿名嵌套类是静态的。 IE。在第三个示例中,在 newJComponent 之间添加一个“静态”。
  • 我在原始问题中添加了一个说明以显示想要的内容。
  • @MichaelMyers,JLS 中的听写总是需要解释的。
【解决方案5】:

关于通过在静态方法中调用匿名内部类来使它们成为静态的注意事项。

这实际上并没有删除引用。您可以通过尝试序列化匿名类而不使封闭类可序列化来测试这一点。

【讨论】:

  • -1:在静态方法中创建匿名类实际上确实删除了对外部类的引用。您可以通过尝试序列化匿名类而不使封闭类可序列化来测试这一点。 (我刚做了。)
【解决方案6】:

匿名内部类永远不是静态的(它们不能声明静态方法或非最终静态字段),但如果它们是在静态上下文(静态方法或静态字段)中定义的,它们在某种意义上表现为静态无法访问封闭类的非静态(即实例)成员(就像静态上下文中的其他所有内容一样)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多