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 :
- Héritons-nous d'une classe imbriquée d'une classe ou héritons-nous d'une autre classe d'une classe imbriquée ?
- Le successeur/hérité est-il une classe publique régulière ou est-ce également une classe imbriquée ?
- Enfin, quel type exactement de classes imbriquées utilisons-nous dans toutes ces situations ?
Classes imbriquées statiques
Leurs 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
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 Drawing
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 constater, aucun problème. Nous pouvons supprimer complètement la classe Drawing
et 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 Boeing737Drawing
de 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 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 classe Boeing737Drawing
hé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 Boeing737Drawing
de la classe Boeing737
et 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 maxPassengersCount
publique 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. Encore 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 :
- Cours régulier.
- Une classe interne déclarée dans la même classe que la classe locale ou chez ses ancêtres.
- Depuis une autre classe locale déclarée dans la même méthode (bloc).
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 PhoneNumber
de 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 PhoneNumber
encore plus haut dans la chaîne de l’héritage. Déclarons une classe abstraite AbstractPhoneNumberValidator
qui 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, PhoneNumberValidator
les 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
}
}
Bicycle
Ici, 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 SportSeat
avions interne. Mais que se passe-t-il si nous décidons d’en faire SportSeat
une 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, Bicycle
vous 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 SportSeat
une référence à l'objet au constructeur. Bicycle
Voici 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 ! :)
GO TO FULL VERSION