Est-il possible de rendre statiques des classes internes anonymes en Java?


123

En Java, les classes imbriquées peuvent être l'une staticou l' autre . S'ils le sont static, ils ne contiennent pas de référence au pointeur de l'instance contenant (ils ne sont plus appelés classes internes, ils sont appelés classes imbriquées).

Oublier de créer une classe imbriquée staticalors qu'elle n'a pas besoin de cette référence peut entraîner des problèmes de récupération de place ou d'analyse d'échappement.

Est-il possible de créer également une classe interne anonyme static? Ou le compilateur le comprend-il automatiquement (ce qu'il pourrait, car il ne peut pas y avoir de sous-classes)?

Par exemple, si je fais un comparateur anonyme, je n'ai presque jamais besoin de la référence à l'extérieur:

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

Quels sont les problèmes avec "garbage collection ou analyse d'échappement" lorsque l'on oublie de rendre statique une classe interne? Je pensais que ce n'était qu'une question de performance ...
Tim Büthe

17
Votre instance de classe interne conserve une référence à son instance externe active, même si vous n'en avez pas besoin. Cela pourrait empêcher des choses d'être ramassées. Imaginez un objet de fabrique (lourd en ressources) qui crée des instances légères de quelque chose. Une fois que l'usine a fait son travail (par exemple au démarrage de l'application), elle peut être éliminée, mais cela ne fonctionne que si les éléments qu'elle a créés ne sont pas liés.
Thilo

Je sais, ce n'est qu'un exemple, mais comme il s'agit d'un exemple récurrent, il faut mentionner que cela Collections.sort(list, String.CASE_INSENSITIVE_ORDER)fonctionne depuis Java 2, lire, puisque l'API Collection existe…
Holger

Réponses:


138

Non, vous ne pouvez pas, et non, le compilateur ne peut pas le comprendre. C'est pourquoi FindBugs suggère toujours de changer les classes internes anonymes en classes staticimbriquées nommées si elles n'utilisent pas leur thisréférence implicite .

Edit: Tom Hawtin - tackline dit que si la classe anonyme est créée dans un contexte statique (par exemple dans la mainméthode), la classe anonyme l'est en fait static. Mais le JLS n'est pas d'accord :

Une classe anonyme n'est jamais abstract(§8.1.1.1). Une classe anonyme est toujours une classe interne (§8.1.3); ce n'est jamais static(§8.1.1, §8.5.1). Une classe anonyme est toujours implicitement final(§8.1.1.2).

Le glossaire Java de Roedy Green dit que le fait que les classes anonymes soient autorisées dans un contexte statique dépend de l'implémentation:

Si vous voulez dérouter ceux qui gèrent votre code, wags a découvert javac.exequ'il autorisera les classes anonymes à l'intérieur du staticcode et des staticméthodes d' initialisation , même si la spécification du langage dit que les classes anonymes ne le sont jamais static. Ces classes anonymes, bien sûr, n'ont pas accès aux champs d'instance de l'objet. Je ne recommande pas de faire ça. La fonction peut être retirée à tout moment.

Edit 2: Le JLS couvre en fait les contextes statiques plus explicitement au §15.9.2 :

Soit C la classe en cours d'instanciation, et soit i l'instance en cours de création. Si C est une classe interne, alors i peut avoir une instance immédiatement englobante. L'instance immédiatement englobante de i (§8.1.3) est déterminée comme suit.

  • Si C est une classe anonyme, alors:
    • Si l'expression de création d'instance de classe se produit dans un contexte statique (§8.1.3), alors i n'a pas d'instance immédiatement englobante.
    • Sinon, l'instance immédiatement englobante de i est this.

Ainsi, une classe anonyme dans un contexte statique équivaut à peu près à une staticclasse imbriquée en ce qu'elle ne garde pas de référence à la classe englobante, même si ce n'est techniquement pas une staticclasse.


19
+1 pour FindBugs - chaque développeur Java devrait l'avoir dans sa version.
Andrew Duffy le

13
C'est très malheureux, car cela signifie que vous voudrez peut-être éviter cette syntaxe sinon presque concise pour des raisons de performances.
Thilo

2
JLS 3rd Ed traite le cas des classes internes dans des contextes statiques. Ils ne sont pas statiques au sens JLS, mais le sont au sens donné dans la question.
Tom Hawtin - tackline

6
Voici un exemple de la façon dont il dépend de l'implémentation: ce code s'imprime en trueutilisant javac (sun-jdk-1.7.0_10) et en falseutilisant le compilateur Eclipse.
Paul Bellora

1
@MichaelMyers J'ai essayé de simuler les FindBugs qui m'ont alerté de faire un Anonymous Inner sans utiliser la référence «this», et rien ne se passe. Pouvez-vous démontrer comment FindBugs vous alerte comme vous l'avez dit au début de votre réponse? Collez simplement un lien ou quoi que ce soit.
Thufir Hawat

15

En quelque sorte. Une classe interne anonyme créée dans une méthode statique sera évidemment effectivement statique car il n'y a pas de source pour un this externe.

Il existe des différences techniques entre les classes internes dans les contextes statiques et les classes imbriquées statiques. Si vous êtes intéressé, lisez le JLS 3rd Ed.


En fait, je reprends ça; le JLS n'est pas d'accord. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Une classe anonyme est toujours une classe interne; elle n'est jamais statique."
Michael Myers

1
statique dans un sens différent de celui de la question.
Tom Hawtin - tackline

1
J'ai ajouté une petite précision.
Tom Hawtin - tackline

15

Je pense qu'il y a un peu de confusion dans la nomenclature ici, qui est certes trop stupide et déroutante.

Quel que soit votre nom, ces modèles (et quelques variantes avec une visibilité différente) sont tous possibles, normaux et légaux en 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() {
      ...
    }
  }
}

Ils sont pris en compte dans la spécification du langage (si vous êtes vraiment dérangé, voyez la section 15.9.5.1 pour celle à l'intérieur de la méthode statique).

Mais cette citation est tout simplement fausse :

javac.exe autorisera les classes anonymes dans le code d'initialisation statique et les méthodes statiques, même si la spécification du langage indique que les classes anonymes ne sont jamais statiques

Je pense que l'auteur cité confond le mot-clé statique avec le contexte statique . (Certes, le JLS est également un peu déroutant à cet égard.)

Honnêtement, tous les modèles ci-dessus sont bons (peu importe ce que vous les appelez "imbriqué", "intérieur", "anonyme" ...). Vraiment, personne ne supprimera soudainement cette fonctionnalité dans la prochaine version de Java. Honnêtement!


2
"(Certes, le JLS est également un peu déroutant à cet égard.)" Vous avez raison. Cela semblait étrange de dire que cela dépend de l'implémentation, mais je ne me souviens pas avoir vu des erreurs évidentes dans le glossaire Java auparavant. Désormais, je le prends avec un grain de sel.
Michael Myers

2
Nous ne parlons en fait d'aucun des modèles. Nous voulons dire que la classe imbriquée anonyme est statique. Ie ajouter un "statique" entre newet JComponentdans votre troisième exemple.
Timmmm

J'ai ajouté une clarification à la question initiale pour montrer ce que l'on voulait.
Timmmm

@MichaelMyers, La dictée en JLS doit toujours être interprétée.
Pacerier


0

les classes internes anonymes ne sont jamais statiques (elles ne peuvent pas déclarer de méthodes statiques ou de champs statiques non finaux), mais si elles sont définies dans un contexte statique (méthode statique ou champ statique), elles se comportent comme statiques dans le sens où elles ne peuvent pas accéder aux membres non statiques (c.-à-d. instance) de la classe englobante (comme tout le reste à partir d'un contexte statique)


-3

Sur la note de rendre statique une classe interne anonyme en les appelant dans une méthode statique.

Cela ne supprime pas réellement la référence. Vous pouvez tester cela en essayant de sérialiser la classe anonyme et en ne rendant pas la classe englobante sérialisable.


5
-1: Création d' une classe anonyme au sein d' une méthode statique effectivement ne supprime la référence à la classe externe. Vous pouvez tester cela en essayant de sérialiser la classe anonyme et en ne rendant pas la classe englobante sérialisable. (Je viens de le faire.)
Christian Semrau
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.