Ist es möglich, anonyme innere Klassen in Java statisch zu machen?

123

In Java können verschachtelte Klassen entweder sein staticoder nicht. Wenn staticdies der Fall ist , enthalten sie keinen Verweis auf den Zeiger der enthaltenen Instanz (sie werden auch nicht mehr als innere Klassen bezeichnet, sondern als verschachtelte Klassen).

Das Vergessen, eine verschachtelte Klasse zu erstellen, staticwenn diese Referenz nicht benötigt wird, kann zu Problemen bei der Speicherbereinigung oder der Escape-Analyse führen.

Ist es möglich, auch eine anonyme innere Klasse staticzu bilden? Oder findet der Compiler dies automatisch heraus (was er könnte, weil es keine Unterklassen geben kann)?

Wenn ich zum Beispiel einen anonymen Komparator mache, brauche ich fast nie den Verweis nach außen:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }
Thilo
quelle
Was sind die Probleme mit "Garbage Collection oder Escape-Analyse", wenn vergessen wird, eine innere Klasse statisch zu machen? Ich dachte, hier geht es nur um Leistung ...
Tim Büthe
17
Ihre innere Klasseninstanz hält einen Verweis auf ihre äußere Instanz am Leben, auch wenn Sie ihn nicht benötigen. Dies könnte verhindern, dass Sachen vom Müll gesammelt werden. Stellen Sie sich ein (ressourcenintensives) Factory-Objekt vor, das leichte Instanzen von etwas erstellt. Nachdem die Fabrik ihre Arbeit erledigt hat (z. B. während des Starts der Anwendung), kann sie entsorgt werden. Dies funktioniert jedoch nur, wenn die von ihr erstellten Elemente nicht miteinander verknüpft werden.
Thilo
Ich weiß, dies ist nur ein Beispiel, aber da es sich um ein wiederkehrendes handelt, sollte erwähnt werden, dass es Collections.sort(list, String.CASE_INSENSITIVE_ORDER)seit Java 2 funktioniert, gelesen, da die Sammlungs-API existiert…
Holger

Antworten:

138

Nein, das können Sie nicht, und nein, der Compiler kann es nicht herausfinden. Aus diesem Grund schlägt FindBugs immer vor, anonyme innere Klassen in benannte staticverschachtelte Klassen zu ändern, wenn sie ihre implizite thisReferenz nicht verwenden .

Bearbeiten: Tom Hawtin - Tackline sagt, dass, wenn die anonyme Klasse in einem statischen Kontext (z. B. in der mainMethode) erstellt wird, die anonyme Klasse tatsächlich ist static. Aber die JLS ist anderer Meinung :

Eine anonyme Klasse ist niemals abstract(§8.1.1.1). Eine anonyme Klasse ist immer eine innere Klasse (§8.1.3); es ist niemals static(§8.1.1, §8.5.1). Eine anonyme Klasse ist immer implizit final(§8.1.1.2).

Das Java-Glossar von Roedy Green besagt, dass die Tatsache, dass anonyme Klassen in einem statischen Kontext zulässig sind, implementierungsabhängig ist:

Wenn Sie diejenigen verblüffen möchten, die Ihren Code verwalten, haben Wags festgestellt, javac.exedass anonyme Klassen innerhalb des staticInit-Codes und der staticMethoden zulässig sind, obwohl die Sprachspezifikation besagt, dass anonyme Klassen dies niemals tun static. Diese anonymen Klassen haben natürlich keinen Zugriff auf die Instanzfelder des Objekts. Ich empfehle dies nicht. Die Funktion kann jederzeit abgerufen werden.

Edit 2: Das JLS behandelt statische Kontexte in §15.9.2 expliziter :

Sei C die Klasse, die instanziiert wird, und sei i die Instanz, die erstellt wird. Wenn C eine innere Klasse ist, habe ich möglicherweise eine sofort einschließende Instanz. Die unmittelbar einschließende Instanz von i (§8.1.3) wird wie folgt bestimmt.

  • Wenn C eine anonyme Klasse ist, dann:
    • Wenn der Ausdruck zur Erstellung einer Klasseninstanz in einem statischen Kontext auftritt (§8.1.3), habe ich keine unmittelbar einschließende Instanz.
    • Andernfalls wird die unmittelbar umgebende Instanz von i ist this.

Eine anonyme Klasse in einem statischen Kontext entspricht also in etwa einer staticverschachtelten Klasse, da sie keinen Verweis auf die einschließende Klasse enthält, obwohl es sich technisch gesehen nicht um eine staticKlasse handelt.

Michael Myers
quelle
19
+1 für FindBugs - jeder Java-Entwickler sollte dies in seinem Build haben.
Andrew Duffy
13
Das ist sehr bedauerlich, da Sie diese ansonsten fast prägnante Syntax aus Leistungsgründen vermeiden möchten.
Thilo
2
JLS 3rd Ed befasst sich mit dem Fall innerer Klassen in statischen Kontexten. Sie sind nicht statisch im Sinne von JLS, sondern statisch im Sinne der Frage.
Tom Hawtin - Tackline
6
Hier ist ein Beispiel dafür , wie es der Umsetzung abhängig: Dieser Code druckt truemit javac (sun-jdk-1.7.0_10) und falsemit Eclipse - Compiler.
Paul Bellora
1
@MichaelMyers Ich habe versucht, die FindBugs zu simulieren, um mich darauf aufmerksam zu machen, dass ich ein anonymes Inneres mache, ohne die 'this'-Referenz zu verwenden, und nichts passiert. Können Sie zeigen, wie FindBugs Sie alarmiert, wie Sie zu Beginn Ihrer Antwort gesagt haben? Fügen Sie einfach einen Link ein oder was auch immer.
Thufir Hawat
15

So'ne Art. Eine anonyme innere Klasse, die in einer statischen Methode erstellt wurde, ist offensichtlich effektiv statisch, da es keine Quelle für eine äußere Klasse gibt.

Es gibt einige technische Unterschiede zwischen inneren Klassen in statischen Kontexten und statisch verschachtelten Klassen. Wenn Sie interessiert sind, lesen Sie die JLS 3rd Ed.

Tom Hawtin - Tackline
quelle
Eigentlich nehme ich das zurück; Die JLS ist anderer Meinung. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Eine anonyme Klasse ist immer eine innere Klasse; sie ist niemals statisch."
Michael Myers
1
statisch in einem anderen Sinne als in der Frage.
Tom Hawtin - Tackline
1
Ich habe eine kleine Klarstellung hinzugefügt.
Tom Hawtin - Tackline
15

Ich denke, die Nomenklatur hier ist etwas verwirrend, was zugegebenermaßen zu albern und verwirrend ist.

Wie auch immer Sie sie nennen, diese Muster (und einige Variationen mit unterschiedlicher Sichtbarkeit) sind alle möglich, normales, legales 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() {
      ...
    }
  }
}

Sie werden in der Sprachspezifikation berücksichtigt (wenn Sie wirklich gestört sind, finden Sie in Abschnitt 15.9.5.1 die Informationen zur statischen Methode).

Aber dieses Zitat ist einfach falsch :

javac.exe erlaubt anonyme Klassen innerhalb des statischen Init-Codes und der statischen Methoden, obwohl die Sprachspezifikation besagt, dass anonyme Klassen niemals statisch sind

Ich denke, der zitierte Autor verwechselt das statische Schlüsselwort mit dem statischen Kontext . (Zugegeben, das JLS ist auch in dieser Hinsicht etwas verwirrend.)

Ehrlich gesagt sind alle oben genannten Muster in Ordnung (wie auch immer Sie sie "verschachtelt", "inner", "anonym" nennen, was auch immer ...). Wirklich, niemand wird diese Funktionalität in der nächsten Version von Java plötzlich entfernen. Ehrlich!

Neil Coffey
quelle
2
"(Zugegeben, die JLS ist auch in dieser Hinsicht etwas verwirrend.)" Sie haben das richtig verstanden. Es klang seltsam zu sagen, dass es von der Implementierung abhängt, aber ich kann mich nicht erinnern, zuvor offensichtliche Fehler im Java-Glossar gesehen zu haben. Von nun an nehme ich es mit einem Körnchen Salz.
Michael Myers
2
Wir sprechen eigentlich nicht über eines der Muster. Wir meinen, dass die anonyme verschachtelte Klasse statisch ist. Fügen Sie also zwischen newund JComponentin Ihrem dritten Beispiel eine "statische" hinzu .
Timmmm
Ich habe der ursprünglichen Frage eine Klarstellung hinzugefügt, um zu zeigen, was gewünscht wird.
Timmmm
@ MichaelMyers, Das Diktat in JLS muss immer interpretiert werden.
Pacerier
6

Innere Klassen können nicht statisch sein - eine statisch verschachtelte Klasse ist keine innere Klasse. Das Java-Tutorial spricht hier darüber .

Andrew Duffy
quelle
1
Ich habe die Frage unter Bezugnahme auf die offizielle Nomenklatur aktualisiert.
Thilo
0

anonyme innere Klassen sind niemals statisch (sie können keine statischen Methoden oder nicht endgültigen statischen Felder deklarieren), aber wenn sie in einem statischen Kontext (statische Methode oder statisches Feld) definiert sind, verhalten sie sich in dem Sinne statisch, dass sie es nicht können Zugriff auf nicht statische (dh Instanz-) Mitglieder der einschließenden Klasse (wie alles andere aus einem statischen Kontext)

Luca
quelle
-3

Um eine anonyme innere Klasse statisch zu machen, indem Sie sie innerhalb einer statischen Methode aufrufen.

Dadurch wird die Referenz nicht entfernt. Sie können dies testen, indem Sie versuchen, die anonyme Klasse zu serialisieren und die einschließende Klasse nicht serialisierbar zu machen.

Terra Caines
quelle
5
-1: Durch das Erstellen einer anonymen Klasse innerhalb einer statischen Methode wird der Verweis auf die äußere Klasse entfernt. Sie können dies testen, indem Sie versuchen, die anonyme Klasse zu serialisieren und die einschließende Klasse nicht serialisierbar zu machen. (Ich habe es gerade getan.)
Christian Semrau