Hej! I dag skal vi se på en vigtig mekanisme: arv i indlejrede klasser. Har du nogensinde tænkt over, hvad du ville gøre, hvis du skulle få en indlejret klasse til at arve en anden klasse. Hvis ikke, tro mig: denne situation kan være forvirrende, fordi der er mange nuancer.
- Får vi en indlejret klasse til at arve en eller anden klasse? Eller får vi en klasse til at arve en indlejret klasse?
- Er børne-/forældreklassen en almindelig offentlig klasse, eller er det også en indlejret klasse?
- Til sidst, hvilken type indlejrede klasser bruger vi i alle disse situationer?
Statiske indlejrede klasser
Deres arveregler er de enkleste. Her kan du lave næsten alt hvad hjertet begærer. En statisk indlejret klasse kan arve:- en almindelig klasse
- en statisk indlejret klasse, der er erklæret i en ydre klasse eller dens forfædre
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;
}
}
}
Lad os prøve at ændre koden og oprette en Drawing
statisk indlejret klasse og dens efterkommer — 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;
}
}
}
Som du kan se, intet problem. Vi kan endda trække klassen ud Drawing
og gøre den til en almindelig offentlig klasse i stedet for en statisk indlejret klasse - intet vil ændre sig.
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;
}
}
}
Vi forstår dette. Men hvilke klasser kan arve en statisk indlejret klasse? Næsten alle! Indlejret/ikke-indlejret, statisk/ikke-statisk — det er lige meget. Her får vi den Boeing737Drawing
indre klasse til at arve den Drawing
statiske indlejrede klasse:
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;
}
}
}
Du kan oprette en instans af Boeing737Drawing
sådan her:
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());
}
}
Selvom vores Boeing737Drawing
klasse arver en statisk klasse, er den ikke statisk i sig selv! Som et resultat vil den altid have brug for en forekomst af den ydre klasse. Vi kan fjerne Boeing737Drawing
klassen fra Boeing737
klassen og gøre den til en simpel offentlig klasse. Intet ændrer sig. Den kan stadig arve den Drawing
statiske indlejrede klasse.
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;
}
Det eneste vigtige punkt er, at vi i dette tilfælde skal gøre den statiske maxPassengersCount
variabel offentlig. Hvis det forbliver privat, så vil en almindelig offentlig klasse ikke have adgang til det. Vi har fundet ud af statiske klasser! :) Lad os nu gå videre til indre klasser. De findes i 3 typer: simple indre klasser, lokale klasser og anonyme indre klasser. Igen, lad os gå fra simpelt til komplekst :)
Anonyme indre klasser
En anonym indre klasse kan ikke arve en anden klasse. Ingen anden klasse kan arve en anonym klasse. Det kunne ikke være nemmere! :)Lokale klasser
Lokale klasser (i tilfælde af at du har glemt det) erklæres inde i en kodeblok af en anden klasse. Oftest sker dette inde i en eller anden metode i den ydre klasse. Logisk set kan kun andre lokale klasser inden for samme metode (eller kodeblok) arve en lokal klasse. Her er et eksempel:
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
}
}
Dette er koden fra vores lektion om lokale klasser. Vores nummervalideringsklasse har en PhoneNumber
lokal klasse. Hvis vi har brug for det til at repræsentere to adskilte enheder, for eksempel et mobiltelefonnummer og et fastnettelefonnummer, kan vi kun gøre dette inden for den samme metode. Årsagen er enkel: En lokal klasses omfang er begrænset til den metode (kodeblok), hvor den er deklareret. Som følge heraf vil vi ikke være i stand til at bruge det eksternt (inklusive til klassearv). Mulighederne for arv inden for selve lokalklassen er dog meget bredere! En lokal klasse kan arve:
- En almindelig klasse.
- En indre klasse, der er erklæret i samme klasse som den lokale klasse eller i en af dens forfædre.
- En anden lokal klasse erklæret i samme metode (kodeblok).
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
}
}
Her fjernede vi PhoneNumber
klassen fra validatePhoneNumber()
metoden og gjorde den til en indre klasse i stedet for en lokal klasse. Dette forhindrer os ikke i at få vores 2 lokale klasser til at arve det. Eksempel 2 - "... eller i denne klasses forfædre." Nu er dette allerede mere interessant. Vi kan rykke PhoneNumber
endnu højere op i arvekæden. Lad os erklære en abstrakt AbstractPhoneNumberValidator
klasse, som vil blive forfaderen til vores PhoneNumberValidator
klasse:
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;
}
}
}
Som du kan se, erklærede vi det ikke bare – vi flyttede også den PhoneNumber
indre klasse ind i det. Men i dens efterkommer PhoneNumberValidator
kan lokale klasser, der er erklæret i metoder, arve PhoneNumber
uden problemer!
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
}
}
På grund af arveforholdet "ser" de lokale klasser inde i en efterkommerklasse de indre klasser inde i en forfader. Og endelig, lad os gå videre til den sidste gruppe :)
Indre klasser
En indre klasse erklæret i den samme ydre klasse (eller i dens efterkommer) kan arve en anden indre klasse. Lad os undersøge dette ved at bruge vores eksempel med cykler fra lektionen om indre klasser.
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
}
}
Her erklærede vi den Seat
indre klasse inde i Bicycle
klassen. En speciel type racersæde, SportSeat
, arver det. Men vi kunne oprette en separat "racercykel" type og placere den i en separat klasse:
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!");
}
}
}
Dette er også en mulighed. Den indre klasse af efterkommeren ( SportBicycle.SportSeat
) "ser" forfaderens indre klasser og kan arve dem. At arve indre klasser har en meget vigtig egenskab! I de to foregående eksempler SportSeat
var vores klasse en indre klasse. Men hvad nu hvis vi beslutter os for at lave SportSeat
en almindelig offentlig klasse, der samtidig arver den Seat
indre klasse?
// 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!");
}
}
Vi har en fejl! Kan du gætte hvorfor? :) Det hele er ligetil. Da vi talte om den Bicycle.Seat
indre klasse, nævnte vi, at en reference til en instans af den ydre klasse implicit videregives til konstruktøren af den indre klasse. Det betyder, at du ikke kan oprette et Seat
objekt uden at oprette et Bicycle
objekt. Men hvad med oprettelsen af en SportSeat
? I modsætning til Seat
, har den ikke denne indbyggede mekanisme til implicit at sende konstruktøren en reference til en instans af den ydre klasse. S till, uden et Bicycle
objekt, kan vi ikke skabe et SportSeat
objekt, ligesom i tilfældet med Seat
. Derfor er der kun én ting tilbage for os at gøre - eksplicit videregive SportSeat
en reference til et Bicycle
objekt til konstruktøren. Sådan gør du:
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!");
}
}
Vi kalder superklassens konstruktør ved hjælp af super();
Nu, hvis vi vil oprette et SportSeat
objekt, vil intet forhindre os i at gøre dette:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Pyha! Denne lektion var ret lang :) Men du lærte meget! Nu er det tid til at løse nogle opgaver! :)
GO TO FULL VERSION