CodeGym /Blog Java /Random-FR /Classes internes imbriquées
Auteur
John Selawsky
Senior Java Developer and Tutor at LearningTree

Classes internes imbriquées

Publié dans le groupe Random-FR
Salut! Aujourd'hui, nous aborderons un sujet important - le fonctionnement des classes imbriquées en Java. Java vous permet de créer des classes à l'intérieur d'une autre classe :

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Ces classes internes sont dites imbriquées. Ils sont divisés en 2 types :
  1. Classes imbriquées non statiques. Celles-ci sont également appelées classes internes.
  2. Classes imbriquées statiques.
À leur tour, les classes internes ont deux sous-catégories distinctes. En plus d'une classe interne étant simplement une classe interne, elle peut également être :
  • une classe locale
  • une classe anonyme
Confus? :) C'est bon. Voici un schéma pour plus de clarté. Revenez-y pendant la leçon si vous vous retrouvez soudainement confus ! Classes internes imbriquées - 2Dans la leçon d'aujourd'hui, nous aborderons les classes internes (également appelées classes imbriquées non statiques). Elles sont spécialement mises en évidence dans le schéma général pour ne pas vous perdre :) Commençons par la question évidente : pourquoi les appelle-t-on classes « internes » ? La réponse est assez simple : parce qu'ils sont créés à l'intérieur d'autres classes. Voici un exemple:

public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Ici, nous avons la Bicycleclasse. Il a 2 champs et 1 méthode : start(). Classes internes imbriquées - 3Elle diffère d'une classe ordinaire en ce qu'elle contient deux classes : Handlebaret Seat. Leur code est écrit à l'intérieur de la Bicycleclasse. Ce sont des classes à part entière : comme vous pouvez le voir, chacune d'elles a ses propres méthodes. À ce stade, vous pourriez avoir une question : pourquoi diable devrions-nous mettre une classe à l'intérieur d'une autre ? Pourquoi en faire des classes internes ? Eh bien, supposons que nous ayons besoin de classes distinctes pour les concepts de guidon et de siège dans notre programme. Bien entendu, il ne nous est pas nécessaire de les imbriquer ! Nous pouvons faire des cours ordinaires. Par exemple, comme ceci :

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
Très bonne question ! Bien sûr, nous ne sommes pas limités par la technologie. Faire cela est certainement une option. Ici, l'important est davantage la conception correcte des classes du point de vue d'un programme spécifique et de son objectif. Les classes internes servent à séparer une entité qui est inextricablement connectée à une autre entité. Les guidons, les sièges et les pédales sont des composants d'un vélo. Séparés du vélo, ils n'ont pas beaucoup de sens. Si nous avions fait de tous ces concepts des classes publiques séparées, nous aurions eu le code comme celui-ci dans notre programme :

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... La signification de ce code est même difficile à expliquer. Nous avons un guidon vague (Pourquoi est-ce nécessaire ? Aucune idée, pour être honnête). Et cette poignée tourne à droite... toute seule, sans vélo... pour une raison quelconque. En séparant le concept du guidon du concept du vélo, nous avons perdu un peu de logique dans notre programme. En utilisant une classe interne, le code est très différent :

public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
Sortie console :

Seat up! 
Let's go! 
Steer left! 
Steer right!
Maintenant, ce que nous voyons prend tout à coup un sens ! :) Nous avons créé un objet vélo. Nous avons créé deux "sous-objets" de vélo : un guidon et un siège. Nous avons relevé la selle pour plus de confort et c'est parti : pédalage et direction au besoin ! :) Les méthodes dont nous avons besoin sont appelées sur les objets appropriés. Tout est simple et pratique. Dans cet exemple, la séparation du guidon et de la selle améliore l'encapsulation (nous cachons les données sur les pièces du vélo dans la classe concernée) et nous permet de créer une abstraction plus détaillée. Examinons maintenant une situation différente. Supposons que nous voulions créer un programme qui simule un magasin de vélos et des pièces détachées pour vélos. Classes internes imbriquées - 4Dans cette situation, notre solution précédente ne fonctionnera pas. Dans un magasin de vélos, chaque pièce de vélo a du sens même lorsqu'elle est séparée d'un vélo. Par exemple, nous aurons besoin de méthodes telles que "vendre des pédales à un client", "acheter un nouveau siège", etc. Ce serait une erreur d'utiliser des classes internes ici - chaque pièce de vélo individuelle dans notre nouveau programme a une signification propre : il peut être séparé du concept de vélo. C'est précisément ce à quoi vous devez faire attention si vous vous demandez si vous devez utiliser des classes internes ou organiser toutes les entités en classes distinctes. La programmation orientée objet est bonne dans la mesure où elle facilite la modélisation d'entités du monde réel. Cela peut être votre principe directeur lorsque vous décidez d'utiliser ou non des classes internes. Dans un vrai magasin, les pièces de rechange sont séparées des vélos - ce n'est pas grave. Cela signifie qu'il est également acceptable lors de la conception d'un programme. D'accord, nous avons compris la "philosophie" :) Maintenant, familiarisons-nous avec les caractéristiques "techniques" importantes des classes internes. Voici ce que vous devez absolument retenir et comprendre :
  1. Un objet d'une classe interne ne peut exister sans un objet d'une classe externe.

    Cela a du sens : c'est pourquoi nous avons créé les classes Seatet Handlebarinternes dans notre programme - afin de ne pas nous retrouver avec des guidons et des sièges orphelins.

    Ce code ne compile pas :

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    Une autre caractéristique importante en découle :

  2. Un objet d'une classe interne a accès aux variables de la classe externe.

    Par exemple, ajoutons une int seatPostDiametervariable (représentant le diamètre de la tige de selle) à notre Bicycleclasse.

    Ensuite, dans la Seatclasse interne, nous pouvons créer une displaySeatProperties()méthode qui affiche les propriétés du siège :

    
    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    

    Et maintenant nous pouvons afficher ces informations dans notre programme :

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }
    

    Sortie console :

    
    Seat properties: seatpost diameter = 40
    

    Note:la nouvelle variable est déclarée avec le modificateur d'accès le plus strict ( private). Et toujours la classe intérieure a accès !

  3. Un objet d'une classe interne ne peut pas être créé dans une méthode statique d'une classe externe.

    Cela s'explique par les spécificités de l'organisation des classes internes. Une classe interne peut avoir des constructeurs avec des paramètres, ou simplement le constructeur par défaut. Mais quoi qu'il en soit, lorsque nous créons un objet d'une classe interne, une référence à l'objet de la classe externe est transmise de manière invisible à l'objet créé de la classe interne. Après tout, la présence d'une telle référence d'objet est une exigence absolue. Sinon, nous ne pourrons pas créer d'objets de la classe interne.

    Mais si une méthode de la classe externe est statique, alors nous pourrions ne pas avoir d'objet de la classe externe ! Et ce serait une violation de la logique de fonctionnement d'une classe interne. Dans cette situation, le compilateur générera une erreur :

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. Une classe interne ne peut pas contenir de variables et de méthodes statiques.

    La logique est la même : des méthodes et des variables statiques peuvent exister et être appelées ou référencées même en l'absence d'objet.

    Mais sans objet de la classe externe, nous n'aurons pas accès à la classe interne.

    Une contradiction évidente ! C'est pourquoi les variables et méthodes statiques ne sont pas autorisées dans les classes internes.

    Le compilateur générera une erreur si vous essayez de les créer :

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. Lors de la création d'un objet d'une classe interne, son modificateur d'accès est important.

    Une classe interne peut être marquée avec les modificateurs d'accès standard : public, private, protectedet package private.

    Pourquoi est-ce important ?

    Cela affecte où nous pouvons créer des instances de la classe interne dans notre programme.

    Si notre Seatclasse est déclarée comme public, nous pouvons créer Seatdes objets dans n'importe quelle autre classe. La seule exigence est qu'un objet de la classe externe doit également exister.

    Au fait, nous l'avons déjà fait ici :

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }
    

    Nous avons facilement eu accès à la Handlebarclasse intérieure à partir de la Mainclasse.

    Si nous déclarons la classe interne comme private, nous pourrons créer des objets uniquement à l'intérieur de la classe externe.

    On ne peut plus créer d' Seatobjet "à l'extérieur" :

    
    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    Vous comprenez probablement déjà la logique :)

  6. Les modificateurs d'accès pour les classes internes fonctionnent de la même manière que pour les variables ordinaires.

    Le protectedmodificateur donne accès à une variable d'instance dans les sous-classes et les classes qui se trouvent dans le même package.

    protectedfonctionne également pour les classes internes. Nous pouvons créer protecteddes objets de la classe interne :

    • dans la classe extérieure;
    • dans ses sous-classes ;
    • dans les classes qui sont dans le même package.

    Si la classe interne n'a pas de modificateur d'accès ( package private), les objets de la classe interne peuvent être créés :

    • dans la classe extérieure;
    • dans les classes qui sont dans le même package.

    Vous connaissez les modificateurs depuis longtemps, donc pas de problème ici.

C'est tout pour le moment :) Mais ne vous relâchez pas ! Les classes internes sont un sujet assez vaste que nous continuerons à explorer dans la prochaine leçon. Vous pouvez maintenant vous rafraîchir la mémoire de la leçon de notre cours sur les classes internes . Et la prochaine fois, parlons des classes imbriquées statiques.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION