Bună! Astăzi ne vom uita la un mecanism important: moștenirea în clase imbricate. Te-ai gândit vreodată ce ai face dacă ai avea nevoie ca o clasă imbricată să moștenească o altă clasă. Dacă nu, credeți-mă: această situație poate fi confuză, pentru că există o mulțime de nuanțe.
- Facem ca o clasă imbricată să moștenească o clasă? Sau facem ca o clasă să moștenească o clasă imbricată?
- Este clasa copil/părinte o clasă publică obișnuită sau este și o clasă imbricată?
- În sfârșit, ce tip de clase imbricate folosim în toate aceste situații?
Clase imbricate statice
Regulile lor de moștenire sunt cele mai simple. Aici poți face aproape orice îți dorește inima. O clasă imbricată statică poate moșteni:- o clasă obișnuită
- o clasă imbricată statică care este declarată într-o clasă exterioară sau strămoșii acesteia
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;
}
}
}
Să încercăm să schimbăm codul și să creăm o Drawing
clasă imbricată statică și descendentul acesteia — 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;
}
}
}
După cum puteți vedea, nicio problemă. Putem chiar să scoatem Drawing
clasa și să o transformăm într-o clasă publică obișnuită în loc de o clasă imbricată statică - nimic nu se va schimba.
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;
}
}
}
Înțelegem asta. Dar ce clase pot moșteni o clasă imbricată statică? Practic orice! Imbricat/neimbricat, static/non-static — nu contează. Aici facem ca Boeing737Drawing
clasa interioară să moștenească Drawing
clasa imbricată statică:
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;
}
}
}
Puteți crea o instanță Boeing737Drawing
ca aceasta:
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());
}
}
Deși Boeing737Drawing
clasa noastră moștenește o clasă statică, nu este static în sine! Ca rezultat, va avea întotdeauna nevoie de o instanță a clasei exterioare. Putem elimina Boeing737Drawing
clasa din Boeing737
clasă și o putem face o clasă publică simplă. Nimic nu se schimba. Încă poate moșteni Drawing
clasa imbricată statică.
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;
}
Singurul punct important este că în acest caz trebuie să facem maxPassengersCount
publică variabila statică. Dacă rămâne privată, atunci o clasă publică obișnuită nu va avea acces la ea. Ne-am dat seama de clase statice! :) Acum să trecem la clasele interioare. Ele vin în 3 tipuri: clase interioare simple, clase locale și clase interioare anonime. Din nou, să trecem de la simplu la complex :)
Clasele interioare anonime
O clasă interioară anonimă nu poate moșteni o altă clasă. Nicio altă clasă nu poate moșteni o clasă anonimă. Mai simplu nu poate fi! :)Clasele locale
Clasele locale (în cazul în care ați uitat) sunt declarate în interiorul unui bloc de cod al altei clase. Cel mai adesea, acest lucru se întâmplă în interiorul unei metode din clasa exterioară. În mod logic, doar alte clase locale din cadrul aceleiași metode (sau bloc de cod) pot moșteni o clasă locală. Iată un exemplu:
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
}
}
Acesta este codul din lecția noastră despre orele locale. Clasa noastră de validare a numărului are o PhoneNumber
clasă locală. Dacă avem nevoie de el pentru a reprezenta două entități distincte, de exemplu, un număr de telefon mobil și un număr de telefon fix, putem face acest lucru doar în cadrul aceleiași metode. Motivul este simplu: domeniul de aplicare al unei clase locale este limitat la metoda (blocul de cod) în care este declarată. Ca urmare, nu vom putea să-l folosim extern (inclusiv pentru moștenirea clasei). Cu toate acestea, posibilitățile de moștenire în cadrul clasei locale sunt mult mai largi! O clasă locală poate moșteni:
- O clasă obișnuită.
- O clasă interioară care este declarată în aceeași clasă cu clasa locală sau într-unul dintre strămoșii săi.
- O altă clasă locală declarată în aceeași metodă (bloc de cod).
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
}
}
Aici am eliminat PhoneNumber
clasa din validatePhoneNumber()
metodă și am făcut-o o clasă interioară în loc de o clasă locală. Acest lucru nu ne împiedică să facem ca cele 2 clase locale să o moștenească. Exemplul 2 — „... sau în strămoșii acestei clase”. Acum acest lucru este deja mai interesant. Ne putem deplasa PhoneNumber
și mai sus în lanțul moștenirii. Să declarăm o AbstractPhoneNumberValidator
clasă abstractă, care va deveni strămoșul PhoneNumberValidator
clasei noastre:
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;
}
}
}
După cum puteți vedea, nu doar am declarat-o, ci am mutat și PhoneNumber
clasa interioară în ea. Totuși, în descendentul său PhoneNumberValidator
, clasele locale declarate în metode pot moșteni PhoneNumber
fără nicio 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
}
}
Datorită relației de moștenire, clasele locale din interiorul unei clase descendente „văd” clasele interioare din interiorul unui strămoș. Și, în sfârșit, să trecem la ultimul grup :)
Clasele interioare
O clasă interioară declarată în aceeași clasă exterioară (sau în descendentul acesteia) poate moșteni o altă clasă interioară. Să explorăm acest lucru folosind exemplul nostru cu bicicletele din lecția despre orele interioare.
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
}
}
Aici am declarat Seat
clasa interioară în interiorul Bicycle
clasei. Un tip special de scaun de curse, SportSeat
, îl moștenește. Dar, am putea crea un tip separat de „bicicletă de curse” și să-l punem într-o clasă separată:
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!");
}
}
}
Aceasta este, de asemenea, o opțiune. Clasa interioară a descendentului ( SportBicycle.SportSeat
) „vede” clasele interioare ale strămoșului și le poate moșteni. Moștenirea claselor interioare are o caracteristică foarte importantă! În cele două exemple anterioare, SportSeat
clasa noastră a fost o clasă interioară. Dar dacă decidem să facem SportSeat
o clasă publică obișnuită care moștenește simultan Seat
clasa interioară?
// 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!");
}
}
Avem o eroare! Poți ghici de ce? :) Totul este simplu. Când am vorbit despre Bicycle.Seat
clasa interioară, am menționat că o referință la o instanță a clasei exterioare este trecută implicit către constructorul clasei interioare. Aceasta înseamnă că nu puteți crea un Seat
obiect fără a crea un Bicycle
obiect. Dar cum rămâne cu crearea unui SportSeat
? Spre deosebire de Seat
, nu are acest mecanism încorporat pentru a trece implicit constructorului o referință la o instanță a clasei exterioare. Totuși, fără un Bicycle
obiect, nu putem crea un SportSeat
obiect, la fel ca în cazul lui Seat
. Prin urmare, ne rămâne un singur lucru de făcut - transmiteți în mod explicit constructorului SportSeat
o referință la un Bicycle
obiect. Iată cum să o faci:
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!");
}
}
Numim constructorul de superclasă folosind super();
Now, dacă vrem să creăm un SportSeat
obiect, nimic nu ne va împiedica să facem asta:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Pf! Această lecție a fost destul de lungă :) Dar ai învățat multe! Acum este timpul să rezolvi niște sarcini! :)
GO TO FULL VERSION