Classes imbriquées en Java
Classes imbriquées en Java : classes statiques, classes internes, classes locales et classes anonymes.
Java permet de déclarer une classe à l'intérieur d'une autre classe. Le terme générique est classe imbriquée, et Java en propose quatre variantes, distinguées selon leur emplacement et selon qu'elles conservent ou non une référence à une instance englobante :
| Variante | Où elle est déclarée | Référence this englobant ? | Chapitre |
|---|---|---|---|
| Classe statique imbriquée | Dans le corps d'une classe, avec static | Non | ce chapitre |
| Classe interne | Dans le corps d'une classe, sans static | Oui | classes internes |
| Classe locale | Dans le corps d'une méthode | Oui (si méthode non statique) | classes locales |
| Classe anonyme | Une sous-classe/implémentation à la volée | Oui (si contexte non statique) | classes anonymes |
La raison de choisir une classe imbriquée plutôt qu'une classe de premier niveau distincte est la portée — la classe imbriquée n'est utile que dans le contexte de sa classe englobante et ne devrait pas être exposée au reste du code. Ce chapitre est la vue d'ensemble ; chaque type fait l'objet d'un traitement plus approfondi dans les chapitres suivants.
Pourquoi imbriquer des classes ?
Trois motivations principales :
- Regroupement logique. Un
Map.Entryn'a de sens qu'à l'intérieur d'unMap. L'imbriquer rend cette relation évidente dans le code. - Encapsulation. Une classe imbriquée peut être déclarée
privatede sorte que rien en dehors de la classe englobante ne puisse y faire référence. - Fermetures sur l'état englobant. Une classe interne / locale / anonyme peut lire les champs de l'instance englobante et les variables locales de la méthode, ce qui est à la base des gestionnaires d'événements, des itérateurs et de nombreux petits patrons adaptateurs.
Si aucun de ces cas ne s'applique, écrivez une classe de premier niveau.
Classes statiques imbriquées
Une classe marquée static à l'intérieur d'une autre classe est une classe statique imbriquée :
public class Outer {
static class Inner {
void hi() { System.out.println("hi"); }
}
}
Outer.Inner i = new Outer.Inner(); // instantiate directly
i.hi();Une classe statique imbriquée est simplement une classe de premier niveau qui se trouve dans l'espace de noms d'Outer. Elle n'a pas de référence implicite à une instance d'Outer — vous pouvez l'utiliser sans en créer une. La seule différence avec une classe de premier niveau est la portée : Outer.Inner est le nom qualifié.
C'est la variante que vous avez déjà vue tout au long des Parties 5 et 6 — chaque static class Foo {} dans un exemple RunnableJava en est un. Nous avons utilisé static pour pouvoir les instancier depuis un main static sans avoir besoin d'une instance d'Outer.
Classes internes (imbriquées non statiques)
Supprimez static et vous obtenez une classe interne. L'instance de la classe interne est liée à une instance de la classe externe, portant une référence implicite à celle-ci :
public class Outer {
int x = 1;
class Inner { // no static
int get() { return x; } // reads Outer's x through the implicit reference
}
}
Outer o = new Outer();
Outer.Inner i = o.new Inner(); // unusual syntax — bind to o
System.out.println(i.get()); // 1La syntaxe particulière o.new Inner() permet de créer une instance de classe interne liée à une instance externe spécifique. Les classes internes ne peuvent pas avoir de membres static (règle ancienne ; assouplie à partir de Java 16+ pour autoriser les membres static dans les classes internes). Traitement complet dans classes internes.
Classes locales
Une classe déclarée dans le corps d'une méthode est locale — limitée à cette méthode :
public void run() {
class Step { int n; Step(int n) { this.n = n; } } // visible only in run()
Step s = new Step(5);
}Utile pour de petits auxiliaires qui ne méritent pas une classe de premier niveau ni même une classe imbriquée au niveau de la classe. Elles ont accès aux variables final (ou effectivement finales) de la méthode englobante. Le chapitre classes locales donne tous les détails.
Classes anonymes
Une classe anonyme est une sous-classe ou implémentation d'interface définie en ligne au point d'utilisation :
Runnable r = new Runnable() {
@Override
public void run() { System.out.println("hi"); }
};C'est une expression unique qui (1) définit une nouvelle classe implémentant Runnable, (2) en crée une instance, (3) l'assigne à r. La classe n'a pas de nom — elle n'existe qu'ici. Presque tous les cas d'usage des classes anonymes ont été remplacés par les lambdas dans le Java moderne, mais elles restent valides et occasionnellement utiles. Voir classes anonymes.
Choisir le bon type
Un arbre de décision rapide :
- La classe imbriquée a-t-elle besoin d'une référence à une instance externe ? Si non → classe statique imbriquée. Si oui → une variante non statique.
- Est-elle utilisée dans une seule méthode ? Si oui → locale ou anonyme. Si non → interne.
- S'agit-il d'une sous-classe/implémentation d'interface à usage unique avec une ou deux méthodes ? Si oui → lambda (préféré) ou classe anonyme (héritage).
Les classes statiques imbriquées sont de loin les plus courantes. Les classes internes apparaissent pour les adaptateurs de type itérateur. Les classes locales et anonymes sont plus rares dans le code moderne — les lambdas couvrent la plupart de leurs cas d'usage.
Nommage et accès
Les classes imbriquées peuvent porter n'importe quel modificateur d'accès — public, private, protected, package-private — et les règles de modificateur sont les mêmes que pour les membres de premier niveau. Map.Entry est public ; un private static class Node à l'intérieur d'une implémentation de LinkedList est invisible pour le monde extérieur.
Les classes imbriquées compilées reçoivent des noms séparés par $ dans les fichiers .class : Outer$Inner, Outer$1. Vous les verrez dans les traces de pile et les débogueurs à l'occasion.
Un exemple concret
La suite
Les trois prochains chapitres approfondissent chacun des types non statiques. Premier d'entre eux : classes internes, le plus général — une classe liée à une instance englobante.