JavaRush /Blog Java /Random-FR /Classes internes imbriquées ou classe interne en Java

Classes internes imbriquées ou classe interne en Java

Publié dans le groupe Random-FR
Bonjour! Aujourd'hui, nous allons commencer à examiner un sujet important : le fonctionnement des classes imbriquées en Java. En anglais, on les appelle classes imbriquées. Java vous permet de créer certaines classes dans d'autres :
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
Ce sont ces classes qui sont dites imbriquées. Ils sont divisés en 2 types :
  1. Classes imbriquées non statiques - classes imbriquées non statiques. On les appelle également classes internes d'une autre manière.
  2. Classes imbriquées statiques - classes imbriquées statiques.
À leur tour, les classes internes ont deux sous-types spéciaux. Outre le fait qu’une classe interne peut être simplement une classe interne, elle peut aussi être :
  • classe locale
  • cours anonyme
Un peu difficile? :) C'est bon, voici un schéma pour plus de clarté. Revenez-y pendant le cours si vous vous sentez soudain confus ! Classes internes imbriquées - 2Dans la conférence d'aujourd'hui, nous parlerons des classes internes - classes internes (ce sont également des classes imbriquées non statiques, des classes imbriquées non statiques). Elles sont spécialement mises en avant dans le schéma général pour que vous ne vous perdiez pas :) Commençons par la question évidente : pourquoi ces classes sont-elles dites « internes » ? La réponse est assez simple : parce qu’ils sont créés au sein 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("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
Ici, nous avons une classe Bicycle- vélo. Il comporte 2 champs et 1 méthode - start(). Classes internes imbriquées - 3Sa différence avec une classe ordinaire est qu'elle comporte deux classes dont le code est écrit à l'intérieur Bicycle- ce sont les classes HandleBar(volant) et Seat(siège). Ce sont des cours à part entière : comme vous pouvez le constater, chacun d'eux a ses propres méthodes. À ce stade, vous vous posez peut-être une question : pourquoi avons-nous placé une classe dans une autre ? Pourquoi les rendre internes ? D'accord, disons que nous avons besoin de classes séparées pour le volant et le siège dans le programme. Mais vous n’êtes pas obligé de les emboîter ! Vous pouvez faire des cours réguliers. Par exemple, comme ceci :
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is down!");
   }
}
Très bonne question ! Bien entendu, nous n’avons aucune limitation technique – nous pouvons le faire de cette façon. Il s'agit plutôt de concevoir correctement les cours du point de vue d'un programme spécifique et dans le sens de ce programme. Les classes internes sont des classes permettant de mettre en évidence une certaine entité dans un programme inextricablement liée à une autre entité. Le volant, le siège, les pédales sont les composants d'un vélo. Séparés du vélo, ils n’ont aucun sens. Si nous faisions en sorte que toutes ces classes soient des classes publiques séparées, notre programme pourrait avoir, par exemple, le code suivant :
public class Main {

   public static void main(String[] args) {
       HandleBar handleBar = new HandleBar();
       handleBar.right();
   }
}
Ummm... La signification de ce code est même difficile à expliquer. Nous avons un étrange guidon de vélo (pourquoi est-il nécessaire ? Aucune idée, pour être honnête). Et ce volant tourne vers la droite... tout seul, sans vélo... pour une raison quelconque. En séparant l’essence du volant de celle du vélo, nous avons perdu la logique de notre programme. En utilisant une classe interne, le code est complètement 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 de la console :

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
Ce qui se passait prenait soudain un sens ! :) Nous avons créé un objet vélo. Nous avons créé deux de ses « sous-objets » : le volant et le siège. Nous avons surélevé le siège pour plus de commodité - et c'est parti : nous roulons et dirigeons là où nous devons aller ! :) Les méthodes dont nous avons besoin sont appelées sur les objets nécessaires. Tout est simple et pratique. Dans cet exemple, la mise en évidence du guidon et du siège améliore l'encapsulation (nous masquons les données sur les parties du vélo dans la classe correspondante) et nous permet de créer une abstraction plus détaillée. Examinons maintenant une autre situation. Disons que nous voulons créer un programme qui modélise un magasin de vélos et de pièces détachées. Classes internes imbriquées - 4Dans cette situation, notre solution précédente échouera. Dans les limites d'un magasin de pièces détachées, chaque pièce d'un vélo a une signification, même en dehors de l'essence du vélo. Par exemple, nous aurons besoin de méthodes telles que « vendre des pédales à un acheteur », « acheter un nouveau siège », etc. Ce serait une erreur d'utiliser ici des classes internes - chaque partie individuelle du vélo dans notre nouveau programme a sa propre signification : elle est distincte de l'essence du vélo et n'y est en aucun cas liée. C'est à cela que vous devez prêter attention si vous vous demandez si vous devez utiliser des classes internes ou séparer toutes les entités en classes distinctes. La programmation orientée objet est géniale car elle facilite la modélisation d'entités du monde réel. C'est ce que vous pouvez utiliser comme guide pour décider d'utiliser ou non des classes internes. Dans un vrai magasin, les pièces sont séparées des vélos, c'est normal. Cela signifie que cela sera correct lors de la conception d'un programme. Bon, nous avons réglé la « philosophie » :) Faisons maintenant connaissance 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 ».

    C’est logique : c’est pour cela que nous en avons fait des classes Seatinternes HandleBar, afin que les volants et les sièges sans propriétaire n’apparaissent pas ici et là dans notre programme.

    Ce code ne sera pas compilé :

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    Il en découle la caractéristique importante suivante :

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

    Par exemple, ajoutons Bicycleune variable à notre classe int seatPostDiameter: le diamètre de la tige de selle.

    Ensuite, dans la classe interne, Seatnous pouvons créer une méthode getSeatParam()qui nous indiquera le paramètre de 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("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }

    Et maintenant, nous pouvons obtenir 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.getSeatParam();
       }
    }

    Sortie de la console :

    
    Параметр сиденья: диаметр подседельного штыря = 40

    Faites attention:la nouvelle variable est déclarée avec le modificateur le plus strict - private. Et toujours la classe interne y a accès !

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

    Cela s'explique par les caractéristiques de conception des classes internes. Une classe interne peut avoir des constructeurs avec des paramètres ou simplement un constructeur par défaut. Mais indépendamment de cela, lorsque nous créons un objet de la classe interne, une référence à un objet de la classe « externe » lui est discrètement transmise. Après tout, la présence d'un tel objet est une condition préalable. Sinon nous ne pourrons pas créer d’objets de la classe interne.

    Mais si la méthode de la classe externe est statique, alors l’objet de la classe externe peut ne pas exister du tout ! Cela signifie que la logique de la classe interne sera brisée. Dans une telle situation, le compilateur renvoie 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 ici est la même : des méthodes et variables statiques peuvent exister et être appelées même s’il n’y a pas d’objet.

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

    Une contradiction évidente ! Par conséquent, la présence de variables et de méthodes statiques dans les classes internes est interdite.

    Le compilateur générera une erreur en essayant de les créer :

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

    Une classe interne peut être désignée par les modificateurs d'accès standard - public, et .privateprotectedpackage private

    Pourquoi c'est important?

    Cela affecte l'endroit où dans notre programme nous pouvons instancier la classe interne.

    Si notre classe Seatest déclarée comme public, nous pouvons créer ses objets dans n'importe quelle autre classe. La seule exigence est que l'objet de la classe « externe » doit également exister.

    D'ailleurs, 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 accédé à la classe interne HandleBarà partir du fichier Main.

    Si nous déclarons la classe interne comme private, nous n'aurons accès qu'à la création d'objets au sein de la classe « externe ».

    SeatNous ne pourrons plus créer un objet de 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 a 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 normales.

    Le modificateur protecteddonne accès à une variable de classe dans ses classes descendantes et dans les classes qui se trouvent dans le même package.

    La même chose protectedfonctionne pour les classes internes. Les objets protectedde classe interne peuvent être créés :

    • à l'intérieur de la classe « externe » ;
    • dans ses classes descendantes ;
    • 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

    • à l'intérieur de la classe « externe » ;
    • dans les classes qui sont dans le même package.

    Vous connaissez les modificateurs depuis longtemps, il n'y aura donc aucun problème ici.

C'est tout pour l'instant :) Mais ne vous détendez pas ! Les classes imbriquées internes sont un sujet assez vaste que nous continuerons à explorer dans les prochaines leçons. Vous pouvez maintenant réviser le cours magistral sur les cours internes de notre cours. Et la prochaine fois, nous parlerons de classes imbriquées statiques.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION