JavaRush /Blog Java /Random-FR /Héritage des classes imbriquées

Héritage des classes imbriquées

Publié dans le groupe Random-FR
Bonjour! Aujourd'hui, nous examinerons le fonctionnement d'un mécanisme important : l'héritage dans les classes imbriquées. Je ne sais pas si vous avez déjà pensé à ce que vous ferez lorsque vous aurez besoin d’hériter d’une classe imbriquée d’une autre. Sinon, croyez-moi : cette situation peut prêter à confusion, car il y a ici beaucoup de nuances :
  1. Héritons-nous d'une classe imbriquée d'une classe ou héritons-nous d'une autre classe d'une classe imbriquée ?
  2. Le successeur/hérité est-il une classe publique régulière ou est-ce également une classe imbriquée ?
  3. Enfin, quel type exactement de classes imbriquées utilisons-nous dans toutes ces situations ?
Si vous répondez à toutes ces questions, il y aura tellement de réponses possibles que vous aurez la tête qui tournera :) Comme vous le savez, pour résoudre un problème complexe, vous devez le diviser en parties plus simples. C'est ce que nous ferons. Examinons tour à tour chaque groupe de classes imbriquées sous deux angles : qui peut hériter de ce type de classe imbriquée et de qui elle peut hériter. Commençons par les classes imbriquées statiques.

Classes imbriquées statiques

Exemples d'héritage de classes internes - 2Leurs règles de succession sont les plus simples. Ici, vous pouvez faire presque tout ce que votre cœur désire. Une classe imbriquée statique peut être héritée de :
  • cours régulier
  • une classe imbriquée statique déclarée dans la classe externe ou ses ancêtres
Rappelons-nous l'exemple du cours sur les classes imbriquées statiques.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Essayons de changer le code et de créer une classe imbriquée statique Drawinget son descendant - Boeing737Drawing.
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Comme vous pouvez le constater, aucun problème. Nous pouvons supprimer complètement la classe Drawinget en faire une classe publique normale au lieu d'une classe imbriquée statique - rien ne changera.
public class Drawing {

}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
C'est réglé. Et quelles classes peuvent hériter d’une classe imbriquée statique ? Presque n'importe lequel ! Imbriqué/régulier, statique/non statique - cela n'a pas d'importance. Ici, nous héritons de la classe interne Boeing737Drawingde la classe imbriquée statiqueDrawing :
public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
Vous pouvez créer une instance Boeing737Drawingcomme celle-ci :
public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
Bien que notre classe Boeing737Drawinghérite d’une classe statique, elle n’est pas elle-même statique ! Par conséquent, il aura toujours besoin d’une instance de la classe externe. Nous pouvons retirer la classe Boeing737Drawingde la classe Boeing737et en faire simplement une classe publique. Rien ne changera - il peut également hériter d'un fichier Drawing.
public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;

}
Seul point important : dans ce cas il faut rendre maxPassengersCountpublique la variable statique. S'il reste privé, la classe publique normale n'y aura pas accès. Nous avons trié les classes statiques ! :) Passons maintenant aux classes internes. Comme vous vous en souvenez, il en existe 3 types : les classes internes simplement, les classes locales et les classes internes anonymes. Exemples d'héritage de classes internes - 3Encore une fois, passons du simple au complexe :)

Classes internes anonymes

Une classe interne anonyme ne peut pas hériter d’une autre classe. Aucune autre classe ne peut hériter d'une classe anonyme. Rien de plus simple ! :)

Cours locaux

Les classes locales (au cas où vous l'auriez oublié) sont déclarées dans un bloc de code d'une autre classe. Le plus souvent - à l'intérieur d'une méthode de cette classe externe. Il est logique que seules les autres classes locales au sein de la même méthode (ou bloc) puissent hériter d'une classe locale. Voici un exemple :
public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {


       }

       //...code валидации номера
   }
}
C'est le code de notre conférence sur les classes locales. À l’intérieur de la classe du validateur de numéros, nous avons une classe locale PhoneNumber– un numéro de téléphone. Si, pour nos besoins, nous devons en extraire deux entités distinctes, par exemple un numéro de téléphone mobile et un numéro de téléphone fixe, nous ne pouvons le faire qu'avec la même méthode. La raison est simple : la portée d’une classe locale se situe dans la méthode (bloc) où elle est déclarée. Par conséquent, nous ne pourrons pas l'utiliser d'une manière ou d'une autre en externe (y compris pour l'héritage). Cependant, la classe locale elle-même a des possibilités d'héritage plus larges ! Une classe locale peut hériter de :
  1. Cours régulier.
  2. Une classe interne déclarée dans la même classe que la classe locale ou chez ses ancêtres.
  3. Depuis une autre classe locale déclarée dans la même méthode (bloc).
Les premier et troisième points semblent évidents, mais le deuxième est un peu déroutant :/ Regardons deux exemples. Exemple 1 - « hériter d'une classe locale d'une classe interne déclarée dans la même classe que la classe locale » :
public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       //...code валидации номера
   }
}
Ici, nous avons retiré la classe PhoneNumberde la méthode validatePhoneNumber()et l'avons rendue interne au lieu de locale. Cela ne nous empêche pas d'en hériter nos 2 classes locales. Exemple 2 – « ... ou chez les ancêtres de cette classe. » C'est là que ça devient plus intéressant. Nous pouvons l’amener PhoneNumberencore plus haut dans la chaîne de l’héritage. Déclarons une classe abstraite AbstractPhoneNumberValidatorqui deviendra l'ancêtre de la nôtre PhoneNumberValidator:
public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
Comme vous pouvez le voir, nous l'avons non seulement déclaré, mais nous y avons également déplacé la classe interne PhoneNumber. Cependant, dans sa classe descendante, PhoneNumberValidatorles classes locales dans les méthodes peuvent hériter de PhoneNumber!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       //...code валидации номера
   }
}
Grâce à la connexion par héritage, les classes locales à l'intérieur d'une classe descendante « voient » les classes internes à l'intérieur de l'ancêtre. Et enfin, passons au dernier groupe :)

Classes intérieures

Une classe interne peut être héritée par une autre classe interne déclarée dans la même classe externe (ou son descendant). Regardons cela en utilisant notre exemple de vélo tiré de la conférence sur les classes internes.
public class Bicycle {

   private String model;
   private int mawWeight;

   public Bicycle(String model, int mawWeight) {
       this.model = model;
       this.mawWeight = mawWeight;
   }

   public void start() {
       System.out.println("Go!");
   }

   class Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }

   class SportSeat extends Seat {

       //...methods
   }
}
BicycleIci, nous avons déclaré une classe interne à l'intérieur de la classe Seat- siège. Un sous-type spécial de sièges de course en a été hérité - SportSeat. Cependant, nous pourrions créer un type distinct de « vélos de course » et le placer dans une classe distincte :
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int mawWeight) {
       super(model, mawWeight);
   }


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }
}
C'est également possible. La classe interne de l'enfant ( SportBicycle.SportSeat) « voit » les classes internes de l'ancêtre et peut en hériter. L'héritage des classes internes a une caractéristique très importante ! Dans les deux exemples précédents, nous SportSeatavions interne. Mais que se passe-t-il si nous décidons d’en faire SportSeatune classe publique régulière, qui hérite également de la classe interne Seat?
//ошибка! No inclosing instance of  type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
Nous avons une erreur ! Pouvez-vous deviner à quoi cela est lié ? :) C'est simple. Lorsque nous avons parlé de la classe interne Bicycle.Seat, nous avons mentionné que le constructeur de la classe interne transmet implicitement une référence à un objet de la classe externe. Par conséquent, sans créer d'objet, Bicyclevous ne pouvez pas créer d'objet Seat. Et la création SportSeat? Il n'a pas le même mécanisme intégré pour passer implicitement une référence à un objet de classe externe dans le constructeur que dans Seat. Cependant, sans objet Bicycle, comme dans le cas de Seat, nous ne pouvons pas créer d'objet SportSeat. Par conséquent, il ne nous reste qu'une chose à faire : transmettre explicitement SportSeatune référence à l'objet au constructeur. BicycleVoici comment procéder :
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
Pour cela nous utilisons un mot spécial super(); Maintenant, si nous voulons créer un objet SportSeat, rien ne nous empêchera de le faire :
public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
Ouf, la conférence s'est avérée assez importante :) Mais vous avez appris beaucoup de nouvelles choses ! Il est maintenant temps de résoudre quelques problèmes ! :)
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION