Hoi! Vandaag kijken we naar een belangrijk mechanisme: overerving in geneste klassen. Heb je er ooit over nagedacht wat je zou doen als je een geneste klasse een andere klasse zou laten erven? Zo niet, geloof me: deze situatie kan verwarrend zijn, omdat er veel nuances zijn.
- Laten we een geneste klasse een bepaalde klasse erven? Of laten we een klasse een geneste klasse erven?
- Is de child/parent-klasse een gewone openbare klasse of is het ook een geneste klasse?
- Tot slot, welk type geneste klassen gebruiken we in al deze situaties?
Statische geneste klassen
Hun erfenisregels zijn de eenvoudigste. Hier kun je bijna alles doen wat je hartje begeert. Een statische geneste klasse kan erven:- een gewone klas
- een statische geneste klasse die wordt gedeclareerd in een buitenste klasse of zijn voorouders
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;
}
}
}
Laten we proberen de code te wijzigen en een Drawing
statische geneste klasse en zijn afstammeling te maken - 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;
}
}
}
Zoals je ziet, geen probleem. We kunnen zelfs de klasse eruit halen Drawing
en er een gewone openbare klasse van maken in plaats van een statische geneste klasse - er verandert niets.
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;
}
}
}
Wij begrijpen dit. Maar welke klassen kunnen een statische geneste klasse erven? Vrijwel elke! Genest/niet-genest, statisch/niet-statisch — het maakt niet uit. Hier laten we de Boeing737Drawing
innerlijke klasse de Drawing
statische geneste klasse erven:
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;
}
}
}
U kunt als volgt een instantie maken Boeing737Drawing
:
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());
}
}
Hoewel onze Boeing737Drawing
klasse een statische klasse erft, is deze zelf niet statisch! Als gevolg hiervan heeft het altijd een instantie van de buitenste klasse nodig. We kunnen de Boeing737Drawing
klasse uit de Boeing737
klasse verwijderen en er een eenvoudige openbare klasse van maken. Er verandert niets. Het kan nog steeds de Drawing
statische geneste klasse erven.
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;
}
Het enige belangrijke punt is dat we in dit geval de statische variabele openbaar moeten maken maxPassengersCount
. Als het privé blijft, heeft een gewone openbare klasse er geen toegang toe. We hebben statische lessen bedacht! :) Laten we nu verder gaan met innerlijke klassen. Ze zijn er in 3 soorten: eenvoudige innerlijke klassen, lokale klassen en anonieme innerlijke klassen. Nogmaals, laten we van eenvoudig naar complex gaan :)
Anonieme innerlijke klassen
Een anonieme binnenklasse kan geen andere klasse erven. Geen enkele andere klasse kan een anonieme klasse erven. Simpeler kan het niet! :)Lokale klassen
Lokale klassen (voor het geval je het vergeten bent) worden gedeclareerd in een codeblok van een andere klasse. Meestal gebeurt dit binnen een of andere methode van de buitenste klasse. Logischerwijs kunnen alleen andere lokale klassen binnen dezelfde methode (of hetzelfde codeblok) een lokale klasse erven. Hier is een voorbeeld:
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
}
}
Dit is de code van onze les over lokale lessen. Onze nummervalidatieklasse heeft een PhoneNumber
lokale klasse. Als we het nodig hebben om twee verschillende entiteiten weer te geven, bijvoorbeeld een mobiel telefoonnummer en een vast telefoonnummer, kunnen we dit alleen binnen dezelfde methode doen. De reden is eenvoudig: het bereik van een lokale klasse is beperkt tot de methode (codeblok) waar deze wordt gedeclareerd. Als gevolg hiervan kunnen we het niet extern gebruiken (ook niet voor klassenovererving). De mogelijkheden voor overerving binnen de lokale klasse zelf zijn echter veel ruimer! Een lokale klasse kan erven:
- Een gewone klas.
- Een innerlijke klasse die wordt gedeclareerd in dezelfde klasse als de lokale klasse of in een van zijn voorouders.
- Een andere lokale klasse gedeclareerd in dezelfde methode (codeblok).
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
}
}
Hier hebben we de PhoneNumber
klasse uit de validatePhoneNumber()
methode verwijderd en er een innerlijke klasse van gemaakt in plaats van een lokale klasse. Dit weerhoudt ons er niet van om onze 2 lokale klassen het te laten erven. Voorbeeld 2 - "... of in de voorouders van deze klasse." Dit is al interessanter. We kunnen PhoneNumber
nog hoger in de erfenisketen komen. Laten we een abstracte AbstractPhoneNumberValidator
klasse declareren, die de voorouder van onze PhoneNumberValidator
klasse zal worden:
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;
}
}
}
Zoals je kunt zien, hebben we het niet alleen aangekondigd - we hebben ook de PhoneNumber
innerlijke klasse erin verplaatst. In zijn afstammeling kunnen lokale klassen die in methoden zijn gedeclareerd echter probleemloos PhoneNumberValidator
overerven !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);
}
}
// ...number validation code
}
}
Vanwege de overervingsrelatie "zien" de lokale klassen binnen een afstammelingenklasse de innerlijke klassen binnen een voorouder. En tot slot, laten we doorgaan naar de laatste groep :)
Innerlijke klassen
Een innerlijke klasse die is gedeclareerd in dezelfde buitenste klasse (of in zijn afstammeling) kan een andere innerlijke klasse erven. Laten we dit onderzoeken aan de hand van ons voorbeeld met fietsen uit de les over innerlijke klassen.
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
}
}
Hier hebben we de Seat
innerlijke klasse binnen de Bicycle
klasse verklaard. Een speciaal type racestoel, SportSeat
, erft het. Maar we zouden een apart type "racefiets" kunnen maken en deze in een aparte klasse kunnen plaatsen:
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!");
}
}
}
Dit is ook een optie. De innerlijke klasse van de afstammeling ( SportBicycle.SportSeat
) "ziet" de innerlijke klassen van de voorouder en kan deze erven. Het overerven van innerlijke klassen heeft één zeer belangrijke functie! In de vorige twee voorbeelden SportSeat
was onze klas een innerlijke klas. SportSeat
Maar wat als we besluiten een gewone openbare klasse te maken die tegelijkertijd de Seat
innerlijke klasse erft?
// 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!");
}
}
We hebben een fout! Kun je raden waarom? :) Het is allemaal eenvoudig. Toen we het hadden over de Bicycle.Seat
binnenste klasse, vermeldden we dat een verwijzing naar een instantie van de buitenste klasse impliciet wordt doorgegeven aan de constructor van de binnenste klasse. Dit betekent dat u geen object kunt maken Seat
zonder een Bicycle
object te maken. Maar hoe zit het met het maken van een SportSeat
? In tegenstelling tot Seat
, heeft het dit ingebouwde mechanisme niet om de constructor impliciet een verwijzing naar een instantie van de buitenste klasse door te geven. Toch Bicycle
kunnen we zonder object geen SportSeat
object maken, net als in het geval van Seat
. Daarom rest ons nog maar één ding: expliciet SportSeat
een verwijzing naar een object doorgeven aan de constructor Bicycle
. Hier is hoe het te doen:
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!");
}
}
We noemen de superklasse-constructor met super();
Nu, als we een object willen maken SportSeat
, zal niets ons ervan weerhouden dit te doen:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Opluchting! Deze les was vrij lang :) Maar je hebt veel geleerd! Nu is het tijd om enkele taken op te lossen! :)
GO TO FULL VERSION