1. Introduction aux classes anonymes
Supposons que vous avez une classe Animal qui décrit le comportement d’un animal. Cette classe possède une méthode say() qui affiche "L'animal émet un son". Vous devez créer un objet qui se comporte comme un chien et affiche "Ouaf !", mais vous ne voulez pas créer pour cela un fichier séparé Dog.java.
C’est là que les classes anonymes viennent à la rescousse. Ce sont des classes sans nom, déclarées et créées directement à l’endroit où elles sont utilisées. 🚀 Elles permettent d’étendre une classe « à la volée » sans créer de fichier séparé. Au lieu de multiplier de nombreux petits fichiers de classes utilisés dans un seul endroit, vous pouvez simplement écrire toute la logique exactement là où elle est nécessaire.
À quoi ressemble la syntaxe ?
La syntaxe des classes anonymes peut sembler inhabituelle, mais elle est en réalité assez simple :
TipPeremennoy imya = new TipDlyaNasledovaniya() {
// Ici, nous écrivons le corps de la classe anonyme
// Nous redéfinissons les méthodes de la classe parente
};
Décomposons cette syntaxe :
- TipPeremennoy imya : déclaration habituelle d’une variable où nous stockerons notre nouvel objet.
- new TipDlyaNasledovaniya() : ici, on crée comme si c’était un nouvel objet. Mais au lieu d’un nom de classe, on utilise le nom de la classe que l’on veut étendre. Remarquez qu’après les parenthèses () il n’y a pas de point-virgule !
- { ... } : ici, on ouvre des accolades et on écrit toute la logique de notre classe anonyme. On peut redéfinir les méthodes de la classe parente ou ajouter sa propre logique.
Exemple d’héritage d’une classe ordinaire :
Revenons à notre exemple avec l’animal.
class Animal {
void say() {
System.out.println("L'animal émet un son");
}
}
// Nous créons une classe anonyme en étendant Animal
Animal dog = new Animal() {
// Nous redéfinissons la méthode say()
@Override
void say() {
System.out.println("Ouaf-ouaf ! 🐶");
}
};
Animal cat = new Animal() {
@Override
void say() {
System.out.println("Miaou-miaou ! 🐱");
}
};
dog.say(); // Affichera : Ouaf-ouaf ! 🐶
cat.say(); // Affichera : Miaou-miaou ! 🐱
Dans cet exemple, nous avons créé deux objets, dog et cat, qui sont en réalité des classes anonymes étendant Animal. Et nous n’avons créé aucun fichier séparé.
Comparaison des approches
// Méthode classique (on crée une classe séparée)
class Dog extends Animal {
@Override
void say() { System.out.println("Ouaf !"); }
}
Animal dog = new Dog();
// VS
// Classe anonyme (tout au même endroit)
Animal dog = new Animal() {
@Override
void say() { System.out.println("Ouaf !"); }
};
Le résultat est identique, mais la classe anonyme est plus concise et n’encombre pas le projet !
2. Nom de la classe anonyme après compilation
Les classes anonymes n’ont pas de nom dans le code source, mais le compilateur Java doit bien les nommer pour créer le fichier .class. Il le fait selon une règle stricte :
- Le nom du fichier de la classe anonyme est constitué du nom de la classe englobante dans laquelle elle a été déclarée.
- Après le nom de la classe englobante, on ajoute le signe dollar $.
- Vient ensuite le numéro d’ordre de la classe anonyme dans ce fichier, en commençant à 1.
Ainsi, si notre exemple avec les animaux se trouve dans le fichier Main.java, la compilation produira trois fichiers :
- Main.class
- Main$1.class (notre classe anonyme pour le chien)
- Main$2.class (notre classe anonyme pour le chat)
Si la classe anonyme est déclarée à l’intérieur d’une méthode elle-même située dans une classe interne, le nom sera plus complexe, par exemple OuterClass$InnerClass$1.class.
C’est une convention interne du compilateur — utile à connaître, mais rarement importante au quotidien. L’essentiel est de se souvenir qu’une classe anonyme reste une classe à part entière, même si elle n’a pas de nom dans le code source.
3. Caractéristiques importantes et limitations
Les classes anonymes sont un outil puissant, mais elles ont leurs règles.
Accès aux variables. Une classe anonyme peut utiliser des variables de la méthode qui l’entoure. Cependant, ces variables doivent être final ou effectively final (c’est-à-dire que leur valeur ne change pas après l’initialisation).
public void doSomething() {
String greeting = "Salut !"; // Cette variable est effectively final
class OuterClass {
void greet() {
// Nous créons une classe anonyme à l'intérieur de la méthode
new Object() {
void sayHello() {
System.out.println(greeting); // Ceci est autorisé
// greeting = "Au revoir !"; // Ceci provoquera une erreur !
}
}.sayHello();
}
}
new OuterClass().greet();
}
Pourquoi ? Parce qu’une classe anonyme peut « vivre » plus longtemps que la méthode elle‑même, et si elle pouvait modifier la variable, cela poserait des problèmes.
Pas de constructeur. Puisqu’une classe anonyme n’a pas de nom, elle ne peut pas avoir de constructeur. Mais vous pouvez utiliser un bloc d’initialisation pour exécuter du code à la création de l’objet :
Animal dog = new Animal() {
// Bloc d'initialisation
{
System.out.println("Initialisation de la classe anonyme 🐶");
}
@Override
void say() {
System.out.println("Ouaf-ouaf !");
}
};
Limitations. Les classes anonymes ne peuvent pas déclarer de champs statiques (sauf des constantes) ni de méthodes statiques. Elles sont toujours créées comme partie d’un autre objet, elles ne peuvent donc pas être static, public, protected ou private.
4. Subtilités utiles
Quand utiliser les classes anonymes
- Vous devez étendre une classe (ou implémenter une interface) une seule fois.
- L’implémentation est petite — 1–2 méthodes, pas plus que quelques dizaines de lignes.
- La classe n’est nécessaire qu’à un seul endroit et il n’est pas utile de lui donner un nom.
- Vous voulez éviter d’« encombrer » le paquetage avec de nombreuses petites classes jetables.
Cas d’usage typiques :
- Gestionnaires d’événements (GUI, Swing, Android, etc.).
- Passage de callbacks dans des méthodes.
- Implémentation rapide de comparateurs pour trier des collections.
- Objets temporairement modifiés (par exemple pour des tests).
5. Interaction avec la classe englobante
Si une classe anonyme est déclarée dans une méthode non statique ou un bloc de la classe englobante, elle peut accéder aux champs et méthodes de cette classe (y compris privés !).
public class Outer {
private String secret = "Texte secret";
// Classe de base
class Printer {
public void print() {
System.out.println("Affichage classique");
}
}
public void revealSecret() {
Printer p = new Printer() {
@Override
public void print() {
System.out.println("Accès au privé : " + secret);
}
};
p.print();
}
public static void main(String[] args) {
new Outer().revealSecret();
}
}
6. Erreurs courantes lors de l’utilisation des classes anonymes
Erreur n° 1 : tentative de modifier une variable de la méthode englobante.
Si vous avez déclaré une variable en dehors de la classe anonyme et que vous essayez de la modifier après l’avoir utilisée à l’intérieur de la classe anonyme, le compilateur signalera une erreur. La variable doit être final ou effectively final (ne pas changer après l’initialisation).
Erreur n° 2 : une classe anonyme trop volumineuse.
Si une classe anonyme gonfle jusqu’à des dizaines de lignes et contient plusieurs méthodes, c’est le signe qu’il faut l’extraire dans une classe nommée. Sinon, le code deviendra illisible.
Erreur n° 3 : tentative d’utiliser des méthodes ou champs statiques.
Dans une classe anonyme, on ne peut pas déclarer de méthodes ou de champs statiques (à l’exception des constantes). Si c’est vraiment nécessaire, c’est un bon indicateur qu’il faut créer une classe imbriquée normale.
Erreur n° 4 : oubli de la portée.
Une classe anonyme n’est visible qu’à l’endroit où elle est déclarée et n’a pas de nom. En cas de besoin de réutilisation, déclarez une classe ordinaire.
GO TO FULL VERSION