Classes internes, imbriquées et anonymes
Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy — leçon 49 sur 87.
Java vous permet de définir des classes à l'intérieur d'autres classes. Ces classes internes aident à organiser le code en regroupant les fonctionnalités liées et peuvent accéder directement aux membres de la classe externe.
Une classe interne imbriquée est définie comme un membre d'une autre classe. Elle a accès à tous les champs et méthodes de la classe externe, y compris les membres privés :
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Peut accéder au champ privé
}
}
}
// Utilisation
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Affiche : HelloUne classe interne locale est définie à l'intérieur d'une méthode. Elle ne peut être utilisée qu'au sein de cette méthode et peut accéder aux variables locales qui sont effectivement finales :
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Une classe interne anonyme est une classe sans nom, créée et instanciée en une seule expression. Elle est couramment utilisée pour implémenter des interfaces ou étendre des classes à la volée :
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Les classes internes sont particulièrement utiles lorsqu'une classe n'est pertinente que pour une seule autre classe, ce qui permet de garder votre code organisé et encapsulé.
Défi
FacileConstruisons un système de panier d'achat qui présente les trois types de classes internes. Vous allez créer un ShoppingCart qui utilise une classe interne imbriquée pour représenter les articles, une classe interne locale pour calculer les remises, et une classe interne anonyme pour formater le reçu final.
Vous organiserez votre code sur deux fichiers :
ShoppingCart.java: Créez la classe externe qui gère une expérience d'achat. Votre ShoppingCart doit avoir un champ privécustomerName(String).À l'intérieur de ShoppingCart, définissez une classe interne imbriquée appelée
Itemavec deux champs privés :name(String) etprice(double). Donnez à Item un constructeur pour initialiser les deux champs et une méthodegetDetails()qui retourne :[name]: $[price]Votre ShoppingCart a besoin d'un constructeur qui prend le nom du client, et d'une méthode appelée
checkout()qui accepte un Item et un pourcentage de remise (double). À l'intérieur decheckout():- Créez une classe interne locale appelée
DiscountCalculatoravec une méthodeapplyDiscount(double originalPrice)qui retourne le prix après application du pourcentage de remise. - Utilisez la classe locale pour calculer le prix final.
- Créez une classe interne anonyme qui implémente l'interface
Formatter(que vous allez définir) pour formater le reçu. La classe anonyme doit surcharger une méthodeformat()qui retourne une chaîne de reçu multi-ligne.
La méthode
checkout()doit retourner la chaîne du reçu formatée avec ce format exact :--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- Créez une classe interne locale appelée
Formatter.java: Définissez une interface simple avec une seule méthode :String format(). Cette interface sera implémentée par votre classe interne anonyme à l'intérieur de la méthode checkout.Main.java: Donnez vie à votre panier d'achat ! Vous recevrez trois entrées : le nom du client (String), le nom de l'article (String) et le prix de l'article (double). Le pourcentage de remise est fixé à10.0.Créez un ShoppingCart avec le nom du client. Créez ensuite un Item en utilisant la syntaxe de référence de la classe externe :
cart.new Item(name, price). Appelezcheckout()avec l'article et la remise, et affichez le reçu retourné.
Vous recevrez trois entrées dans l'ordre : le nom du client, le nom de l'article et le prix de l'article.
Par exemple, si les entrées sont Alice, Laptop, et 1000.0, le prix final après une remise de 10 % serait 900.0.
Remarquez comment chaque type de classe interne sert un objectif différent : la classe imbriquée regroupe les données liées à la classe externe, la classe locale encapsule la logique nécessaire uniquement au sein d'une méthode, et la classe anonyme fournit une implémentation rapide et unique d'une interface !
Aide-mémoire
Java vous permet de définir des classes à l'intérieur d'autres classes. Ces classes internes aident à organiser le code en regroupant les fonctionnalités liées et peuvent accéder directement aux membres de la classe externe.
Classe interne imbriquée
Une classe interne imbriquée est définie comme un membre d'une autre classe. Elle a accès à tous les champs et méthodes de la classe externe, y compris les membres privés :
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Peut accéder au champ privé
}
}
}
// Utilisation
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // Affiche : HelloClasse interne locale
Une classe interne locale est définie à l'intérieur d'une méthode. Elle ne peut être utilisée qu'au sein de cette méthode et peut accéder aux variables locales qui sont effectivement finales :
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Classe interne anonyme
Une classe interne anonyme est une classe sans nom, créée et instanciée en une seule expression. Elle est couramment utilisée pour implémenter des interfaces ou étendre des classes à la volée :
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Les classes internes sont particulièrement utiles lorsqu'une classe n'est pertinente que pour une seule autre classe, ce qui permet de garder votre code organisé et encapsulé.
Essayez vous-même
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Lire les entrées
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: Créer un ShoppingCart avec le nom du client
// TODO: Créer un Item en utilisant : cart.new Item(itemName, itemPrice)
// TODO: Appeler checkout() avec le item et le discount, puis afficher le résultat
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesChamps (Attributs)Méthode constructeurSurcharge de constructeurRécapitulatif - Calculatrice simple4Héritage
Héritage de base (extends)Le mot-clé superRedéfinition de méthode (@Override)Chaînage de constructeursLa classe ObjectHéritage simple et multiniveauPourquoi pas d'héritage multiple de classesRécapitulatif - Hiérarchie des employés7Méthodes spéciales et classe Object
Méthode toString()equals() et hashCode()Méthode clone()compareTo() et ComparableInterface ComparatorRécapitulatif - Tri personnalisé2Modificateurs d'accès et Encapsulation
Aperçu des niveaux d'accèsMéthodes Getter et SetterMasquage d'informationsLe mot-clé finalRécapitulatif - Gestionnaire de compte bancaire5Polymorphisme
Bases de la surcharge de méthodesRedéfinition de méthodes (Run-Time)Upcasting et DowncastingL'opérateur instanceofClasses et méthodes abstraitesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageAgrégation vs CompositionClasses internes, imbriquées et anonymesEnums et méthodes d'EnumRecords (Java 16+)Classes scellées (Java 17+)11Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron MonteurPatron ObservateurPatron Stratégie3Propriétés de classe et membres statiques
Variables d'instance vs variables statiquesMéthodes statiquesBlocs statiquesConstantes (static final)Récapitulatif - Compteur et utilitaire6Interfaces et Classes Abstraites
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesDefault et Static dans les InterfacesClasses Abstraites vs InterfacesInterfaces FonctionnellesRécapitulatif - Système de Paiement9La généricité
Introduction à la généricitéClasses génériquesMéthodes génériquesParamètres de type bornésWildcards (?, extends, super)Récapitulatif - Conteneur générique12Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositePatron Itérateur