Hej! Idag ska vi titta på en viktig mekanism: arv i kapslade klasser. Har du någonsin tänkt på vad du skulle göra om du behövde få en kapslad klass att ärva någon annan klass. Om inte, tro mig: den här situationen kan vara förvirrande, eftersom det finns många nyanser.
- Får vi en kapslad klass att ärva någon klass? Eller får vi någon klass att ärva en kapslad klass?
- Är barn-/förälderklassen en vanlig offentlig klass, eller är det också en kapslad klass?
- Slutligen, vilken typ av kapslade klasser använder vi i alla dessa situationer?
Statiska kapslade klasser
Deras arvsregler är de enklaste. Här kan du göra nästan allt du kan önska. En statisk kapslad klass kan ärva:- en vanlig klass
- en statisk kapslad klass som deklareras i en yttre klass eller dess förfäder
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;
}
}
}
Låt oss försöka ändra koden och skapa en Drawing
statisk kapslad klass och dess avkomling — 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, inga problem. Vi kan till och med dra ut Drawing
klassen och göra den till en vanlig offentlig klass istället för en statisk kapslad klass - ingenting kommer att förändras.
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 förstår detta. Men vilka klasser kan ärva en statisk kapslad klass? Praktiskt taget vilken som helst! Kapslad/icke-kapslad, statisk/icke-statisk — det spelar ingen roll. Här får vi den Boeing737Drawing
inre klassen att ärva den Drawing
statiska kapslade klassen:
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 skapa en instans av Boeing737Drawing
så här:
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());
}
}
Även om vår Boeing737Drawing
klass ärver en statisk klass, är den inte statisk i sig! Som ett resultat kommer det alltid att behöva en instans av den yttre klassen. Vi kan ta bort Boeing737Drawing
klassen från Boeing737
klassen och göra den till en enkel offentlig klass. Inget ändras. Den kan fortfarande ärva den Drawing
statiska kapslade klassen.
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;
}
Den enda viktiga punkten är att vi i det här fallet måste göra den statiska maxPassengersCount
variabeln offentlig. Om det förblir privat, kommer en vanlig offentlig klass inte att ha tillgång till det. Vi har listat ut statiska klasser! :) Låt oss nu gå vidare till inre klasser. De finns i 3 typer: enkla inre klasser, lokala klasser och anonyma inre klasser. Återigen, låt oss gå från enkla till komplexa :)
Anonyma inre klasser
En anonym inre klass kan inte ärva en annan klass. Ingen annan klass kan ärva en anonym klass. Det kan inte bli enklare! :)Lokala klasser
Lokala klasser (om du har glömt) deklareras i ett kodblock av en annan klass. Oftast händer detta inuti någon metod i den yttre klassen. Logiskt sett kan bara andra lokala klasser inom samma metod (eller kodblock) ärva en lokal klass. Här är ett exempel:
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
}
}
Det här är koden från vår lektion om lokala klasser. Vår nummervalideringsklass har en PhoneNumber
lokal klass. Om vi behöver det för att representera två distinkta enheter, till exempel ett mobiltelefonnummer och ett fast telefonnummer, kan vi bara göra detta inom samma metod. Anledningen är enkel: en lokal klasss omfattning är begränsad till metoden (kodblocket) där den deklareras. Som ett resultat kommer vi inte att kunna använda det externt (inklusive för klassarv). Möjligheterna till arv inom själva lokalklassen är dock mycket bredare! En lokal klass kan ärva:
- En vanlig klass.
- En inre klass som deklareras i samma klass som den lokala klassen eller i någon av dess förfäder.
- En annan lokal klass deklareras i samma metod (kodblock).
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
}
}
Här tog vi bort PhoneNumber
klassen från validatePhoneNumber()
metoden och gjorde den till en inre klass istället för en lokal klass. Detta hindrar oss inte från att få våra två lokala klasser att ärva det. Exempel 2 — "... eller i denna klasss förfäder." Nu är detta redan mer intressant. Vi kan flytta PhoneNumber
ännu högre i arvskedjan. Låt oss förklara en abstrakt klass, som kommer att bli vår klasss AbstractPhoneNumberValidator
förfader :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;
}
}
}
Som du kan se deklarerade vi det inte bara – vi flyttade också in den PhoneNumber
inre klassen till det. Men i dess efterkommande PhoneNumberValidator
kan lokala klasser som deklarerats i metoder ärva PhoneNumber
utan problem!
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 av arvsförhållandet "ser" de lokala klasserna i en ättlingklass de inre klasserna inuti en förfader. Och slutligen, låt oss gå vidare till den sista gruppen :)
Inre klasser
En inre klass som deklareras i samma yttre klass (eller i dess avkomling) kan ärva en annan inre klass. Låt oss utforska detta med vårt exempel med cyklar från lektionen om inre 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
}
}
Här förklarade vi den Seat
inre klassen inne i Bicycle
klassen. En speciell typ av racingstol, , SportSeat
ärver den. Men vi skulle kunna skapa en separat "racingcykel"-typ och placera den i en separat klass:
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!");
}
}
}
Detta är också ett alternativ. Den inre klassen hos efterkommande ( SportBicycle.SportSeat
) "ser" förfaderns inre klasser och kan ärva dem. Att ärva inre klasser har en mycket viktig egenskap! I de två föregående exemplen SportSeat
var vår klass en inre klass. Men tänk om vi bestämmer oss för att göra SportSeat
en vanlig offentlig klass som samtidigt ärver den Seat
inre klassen?
// 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 fick ett fel! Kan du gissa varför? :) Allt är okomplicerat. När vi pratade om den Bicycle.Seat
inre klassen nämnde vi att en referens till en instans av den yttre klassen implicit skickas till konstruktören av den inre klassen. Det betyder att du inte kan skapa ett Seat
objekt utan att skapa ett Bicycle
objekt. Men hur är det med skapandet av en SportSeat
? Till skillnad från Seat
, har den inte denna inbyggda mekanism för att implicit skicka konstruktorn en referens till en instans av den yttre klassen. S till, utan ett Bicycle
objekt, kan vi inte skapa ett SportSeat
objekt, precis som i fallet med Seat
. Därför finns det bara en sak kvar för oss att göra - att uttryckligen skicka SportSeat
en referens till ett Bicycle
objekt till konstruktören. Så här 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 kallar superklassens konstruktor med hjälp av super();
Now, om vi vill skapa ett SportSeat
objekt, kommer ingenting att hindra oss från att göra detta:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Puh! Den här lektionen var ganska lång :) Men du lärde dig mycket! Nu är det dags att lösa några uppgifter! :)
GO TO FULL VERSION