是否可以使Java中的匿名内部类静态化?


123

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

忘记做一个嵌套的类 static在不需要引用时可能会导致垃圾回收或转义分析的问题。

是否可以建立匿名内部类 static?还是编译器会自动找出原因(之所以会这样,因为不能有任何子类)?

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

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

忘记使内部类静态化时,“垃圾回收或转义分析”有什么问题?我以为这只是性能方面的问题……
TimBüthe09年

17
内部类实例使对外部实例的引用保持活动状态,即使您不需要它也是如此。这可以防止东西被垃圾收集。描绘一个(资源丰富的)工厂对象,该对象创建某些对象的轻量级实例。工厂完成工作后(例如,在应用程序启动期间),可以将其丢弃,但是只有在工厂创建的内容没有链接回去的情况下,它才能起作用。
Thilo

我知道,这只是一个例子,但是由于它是一个重复出现的例子,因此应该提到它Collections.sort(list, String.CASE_INSENSITIVE_ORDER)自Java 2读取以来就起作用,因为Collection API存在……
Holger

Answers:


138

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

编辑: Tom Hawtin-tackline说,如果匿名类是在静态上下文中创建的(例如在main方法中),则实际上是匿名类static。但是联合检查组不同意

永远不会有匿名类abstract(第8.1.1.1节)。匿名类始终是内部类(第8.1.3节);从来没有static(§8.1.1,§8.5.1)。匿名类始终是隐式的final(第8.1.1.2节)。

Roedy Green的Java词汇表说,在静态上下文中允许匿名类的事实取决于实现:

如果您想让那些维护您代码的人感到困惑,那么笨拙的发现javac.exe会允许staticinit代码和static方法中包含匿名类,即使语言规范说从来没有匿名类也是如此static。当然,这些匿名类无法访问对象的实例字段。我不建议这样做。该功能可以随时拉出。

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

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

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

因此,静态上下文中的匿名类与static嵌套类大致等效,因为它在技术上不是类,但它不保留对封闭类的引用static


19
+1 for FindBugs-每个Java开发人员都应在自己的版本中添加此代码。
Andrew Duffy

13
这是非常不幸的,因为这意味着出于性能原因,您可能希望避免使用本来几乎简洁的语法。
Thilo

2
JLS 3rd Ed处理静态上下文中内部类的情况。从JLS的意义上说,它们不是静态的,但是从问题中给出的意义上说,它们是静态的。
Tom Hawtin-大头钉

6
这是一个与实现有关的示例:此代码true使用javac(sun-jdk-1.7.0_10)和falseEclipse编译器进行打印。
Paul Bellora

1
@MichaelMyers我曾尝试模拟FindBugs,以提醒我不要使用“ this”引用进行匿名内部处理,但没有任何反应。您能证明FindBugs如何像您在回答开始时所说的那样提醒您吗?只需粘贴一些链接或其他内容即可。
Thufir Hawat,2015年

15

有点儿。用静态方法创建的匿名内部类显然是有效的静态的,因为没有外部this的源。

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


实际上,我收回了这一点。JLS对此表示反对。java.sun.com/docs/books/jls/third%5Fedition/html/…:“匿名类始终是内部类;它永远不是静态的。”
迈克尔·迈尔斯

1
静态与问题的意义不同。
Tom Hawtin-大头钉

1
我加了一点澄清。
Tom Hawtin-抢险活动

15

我认为这里的术语有些混乱,诚然这太愚蠢和令人困惑。

无论您如何称呼它们,这些模式(以及一些具有不同可见性的变体)都是可能的,合法的合法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节以获取static方法中的内容)。

但是这句话是完全错误的

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

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

老实说,上面的所有模式都很好(无论您称其为“嵌套”,“内部”还是“匿名” ...)。确实,没有人会在下一版Java中突然删除此功能。老实说!


2
“(诚然,JLS在这方面也有些混乱。)”您说对了。说它依赖于实现听起来很奇怪,但是我不记得以前在Java术语表中看到过任何明显的错误。从现在开始,我带着一粒盐。
迈克尔·迈尔斯

2
我们实际上不是在谈论任何模式。我们的意思是匿名嵌套类是静态的。IE插件之间的“静态” new,并JComponent在你的第三个例子。
Timmmm 2012年

我对原始问题进行了说明,以显示所需的内容。
Timmmm 2012年

@ MichaelMyers,JLS中的命令总是需要解释。
Pacerier


0

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


-3

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

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


5
-1:在静态方法中创建匿名类实际上确实删除了对外部类的引用。您可以通过尝试序列化匿名类而不使封闭类可序列化来进行测试。(我刚刚做过。)
Christian Semrau 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.