Salut! Aujourd'hui, nous allons nous intéresser à un mécanisme important : l'héritage dans les classes imbriquées. Avez-vous déjà pensé à ce que vous feriez si vous deviez faire en sorte qu'une classe imbriquée hérite d'une autre classe. Sinon, croyez-moi : cette situation peut prêter à confusion, car il y a beaucoup de nuances.
Leurs règles d'héritage sont les plus simples. Ici, vous pouvez faire presque tout ce que votre cœur désire. Une classe imbriquée statique peut hériter :
Encore une fois, passons du simple au complexe :)
- Sommes-nous en train de faire hériter une classe imbriquée d'une classe ? Ou faisons-nous hériter une classe d'une classe imbriquée ?
- La classe enfant/parent est-elle une classe publique ordinaire, ou est-ce également une classe imbriquée ?
- Enfin, quel type de classes imbriquées utilisons-nous dans toutes ces situations ?
Classes imbriquées statiques

- une classe ordinaire
- une classe imbriquée statique qui est déclarée dans une classe externe ou ses ancêtres
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 Drawing
classe imbriquée statique et 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 voir, pas de problème. Nous pouvons même extraire la Drawing
classe et en faire une classe publique ordinaire 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;
}
}
}
Nous comprenons cela. Mais quelles classes peuvent hériter d'une classe imbriquée statique ? Pratiquement n'importe lequel ! Imbriqué/non imbriqué, statique/non statique — cela n'a pas d'importance. Ici, nous faisons en sorte que la Boeing737Drawing
classe interne hérite de la Drawing
classe imbriquée statique :
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 Boeing737Drawing
comme 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 Boeing737Drawing
classe hérite d'une classe statique, elle n'est pas statique elle-même ! Par conséquent, il aura toujours besoin d'une instance de la classe externe. Nous pouvons supprimer la Boeing737Drawing
classe de la Boeing737
classe et en faire une simple classe publique. Rien ne change. Il peut toujours hériter de la Drawing
classe imbriquée statique.
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;
}
Le seul point important est que dans ce cas, nous devons rendre maxPassengersCount
publique la variable statique. S'il reste privé, une classe publique ordinaire n'y aura pas accès. Nous avons compris les classes statiques ! :) Passons maintenant aux classes internes. Ils sont de 3 types : les classes internes simples, les classes locales et les classes internes anonymes. 
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, cela se produit à l'intérieur d'une méthode de la classe externe. Logiquement, seules les autres classes locales à l'intérieur de la même méthode (ou bloc de code) peuvent 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 {
}
// ...number validation code
}
}
C'est le code de notre leçon sur les classes locales. Notre classe de validateur de nombre a une PhoneNumber
classe locale. Si nous en avons besoin pour représenter 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'à l'intérieur de la même méthode. La raison est simple : la portée d'une classe locale est limitée à la méthode (bloc de code) où elle est déclarée. Par conséquent, nous ne pourrons pas l'utiliser en externe (y compris pour l'héritage de classe). Cependant, les possibilités d'héritage au sein de la classe locale elle-même sont beaucoup plus larges ! Une classe locale peut hériter :
- Une classe ordinaire.
- Une classe interne qui est déclarée dans la même classe que la classe locale ou dans l'un de ses ancêtres.
- Une autre classe locale déclarée dans la même méthode (bloc de code).
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);
}
}
// ...number validation code
}
}
Ici, nous avons supprimé la PhoneNumber
classe de la validatePhoneNumber()
méthode et en avons fait une classe interne au lieu d'une classe locale. Cela ne nous empêche pas d'en faire hériter nos 2 classes locales. Exemple 2 — "... ou dans les ancêtres de cette classe." Maintenant, c'est déjà plus intéressant. On peut monter PhoneNumber
encore plus haut dans la chaîne d'héritage. Déclarons une AbstractPhoneNumberValidator
classe abstraite, qui deviendra l'ancêtre de notre PhoneNumberValidator
classe :
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 ne nous sommes pas contentés de le déclarer, nous PhoneNumber
y avons également déplacé la classe interne. Cependant, dans son descendant PhoneNumberValidator
, les classes locales déclarées dans les méthodes peuvent hériter PhoneNumber
sans problème !
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);
}
}
// ...number validation code
}
}
En raison de la relation d'héritage, les classes locales à l'intérieur d'une classe descendante "voient" les classes internes à l'intérieur d'un ancêtre. Et enfin, passons au dernier groupe :)
Classes intérieures
Une classe interne déclarée dans la même classe externe (ou dans sa descendante) peut hériter d'une autre classe interne. Explorons cela en utilisant notre exemple avec des vélos de la leçon sur les classes internes.
public class Bicycle {
private String model;
private int maxWeight;
public Bicycle(String model, int maxWeight) {
this.model = model;
this.maxWeight = maxWeight;
}
public void start() {
System.out.println("Let's go!");
}
class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
class SportSeat extends Seat {
// ...methods
}
}
Ici, nous avons déclaré la Seat
classe interne à l'intérieur de la Bicycle
classe. Un type spécial de siège de course, SportSeat
, en hérite. Mais, nous pourrions créer un type "vélo de course" distinct et le mettre dans une classe distincte :
public class SportBicycle extends Bicycle {
public SportBicycle(String model, int maxWeight) {
super(model, maxWeight);
}
class SportSeat extends Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
C'est aussi une option. La classe interne du descendant ( 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, notre SportSeat
classe était une classe interne. Mais que se passe-t-il si nous décidons de créer SportSeat
une classe publique ordinaire qui hérite simultanément de la Seat
classe interne ?
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {
public SportSeat() {
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Nous avons une erreur ! Pouvez-vous deviner pourquoi? :) Tout est simple. Lorsque nous avons parlé de la Bicycle.Seat
classe interne, nous avons mentionné qu'une référence à une instance de la classe externe est passée implicitement au constructeur de la classe interne. Cela signifie que vous ne pouvez pas créer un Seat
objet sans créer un Bicycle
objet. Mais qu'en est-il de la création d'un SportSeat
? Contrairement à Seat
, il n'a pas ce mécanisme intégré pour transmettre implicitement au constructeur une référence à une instance de la classe externe. Pourtant, sans Bicycle
objet, nous ne pouvons pas créer d' SportSeat
objet, tout comme dans le cas de Seat
. Par conséquent, il ne nous reste plus qu'une chose à faire : passer explicitement au SportSeat
constructeur une référence à un Bicycle
objet. Voici comment procéder :
class SportSeat extends Bicycle.Seat {
public SportSeat(Bicycle bicycle) {
bicycle.super();
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Nous appelons le constructeur de la superclasse en utilisant super();
Now, si nous voulons créer un SportSeat
objet, 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);
}
}
Phew! Cette leçon a été plutôt longue :) Mais tu as beaucoup appris ! Il est maintenant temps de résoudre certaines tâches ! :)
GO TO FULL VERSION