Menu
Coddy logo textTech

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 : Hello

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);
}

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é.

challenge icon

Défi

Facile

Construisons 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 Item avec deux champs privés : name (String) et price (double). Donnez à Item un constructeur pour initialiser les deux champs et une méthode getDetails() 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 de checkout() :

    1. Créez une classe interne locale appelée DiscountCalculator avec une méthode applyDiscount(double originalPrice) qui retourne le prix après application du pourcentage de remise.
    2. Utilisez la classe locale pour calculer le prix final.
    3. 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éthode format() 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]
  • 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). Appelez checkout() 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 : Hello

Classe 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
        
    }
}
quiz iconTestez-vous

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