Szia! Ma egy fontos mechanizmust fogunk megvizsgálni: az öröklődést a beágyazott osztályokban. Gondolt már arra, hogy mit tenne, ha egy beágyazott osztályt szeretne örökölni egy másik osztályt. Ha nem, hidd el: ez a helyzet zavaró lehet, mert nagyon sok árnyalat van.
- Egy beágyazott osztályt örökölünk valamilyen osztályra? Vagy valamilyen osztályt örökölünk egy beágyazott osztályba?
- A gyermek/szülő osztály közönséges nyilvános osztály, vagy egyben beágyazott osztály?
- Végül, milyen típusú beágyazott osztályokat használunk ezekben a helyzetekben?
Statikus beágyazott osztályok
Öröklési szabályaik a legegyszerűbbek. Itt szinte bármit megtehetsz, amire a szíved vágyik. Egy statikus beágyazott osztály örökölheti:- hétköznapi osztály
- statikus beágyazott osztály, amely egy külső osztályban vagy annak őseiben van deklarálva
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;
}
}
}
Próbáljuk meg megváltoztatni a kódot, és létrehozni egy Drawing
statikus beágyazott osztályt és leszármazottját — 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;
}
}
}
Amint látja, nincs probléma. Akár ki is húzhatjuk az Drawing
osztályt, és közönséges nyilvános osztállyá tehetjük statikus beágyazott osztály helyett – semmi sem fog változni.
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;
}
}
}
Ezt megértjük. De milyen osztályok örökölhetnek egy statikus beágyazott osztályt? Gyakorlatilag bármelyik! Beágyazott/nem beágyazott, statikus/nem statikus – mindegy. Itt a Boeing737Drawing
belső osztály örökli a Drawing
statikus beágyazott osztályt:
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;
}
}
}
Létrehozhat egy Boeing737Drawing
ilyen példányt:
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());
}
}
Bár a mi Boeing737Drawing
osztályunk örököl egy statikus osztályt, maga nem statikus! Ennek eredményeként mindig szüksége lesz a külső osztály egy példányára. Eltávolíthatjuk az Boeing737Drawing
osztályt az Boeing737
osztályból, és egyszerű nyilvános osztályt készíthetünk belőle. Semmi sem változik. Továbbra is örökölheti a Drawing
statikus beágyazott osztályt.
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;
}
Az egyetlen fontos pont az, hogy ebben az esetben nyilvánossá kell tennünk a statikus maxPassengersCount
változót. Ha privát marad, akkor egy közönséges nyilvános osztály nem fér hozzá. Kitaláltuk a statikus osztályokat! :) Most pedig térjünk át a belső órákra. Három típusuk van: egyszerű belső osztályok, helyi osztályok és névtelen belső osztályok. Ismét térjünk át az egyszerűről a bonyolultra :)
Névtelen belső osztályok
Egy névtelen belső osztály nem örökölhet másik osztályt. Más osztály nem örökölhet névtelen osztályt. Ennél egyszerűbb nem is lehetne! :)Helyi osztályok
A helyi osztályok (ha elfelejtették) egy másik osztály kódblokkjában vannak deklarálva. Leggyakrabban ez a külső osztály valamelyik metódusán belül történik. Logikailag csak az ugyanazon a metóduson (vagy kódblokkon) belüli más helyi osztályok örökölhetnek egy helyi osztályt. Íme egy példa:
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
}
}
Ez a kód a helyi osztályokról szóló leckénkből. Számellenőrző osztályunknak van egy PhoneNumber
helyi osztálya. Ha arra van szükségünk, hogy két különálló entitást képviseljen, például egy mobiltelefon-számot és egy vezetékes telefonszámot, akkor ezt csak ugyanazon a módszeren belül tehetjük meg. Az ok egyszerű: egy helyi osztály hatóköre arra a metódusra (kódblokkra) korlátozódik, ahol deklarálva van. Ennek eredményeként nem fogjuk tudni használni külsőleg (beleértve az osztályöröklést sem). Magán a helyi osztályon belül azonban az öröklés lehetőségei sokkal szélesebbek! Egy helyi osztály örökölheti:
- Közönséges osztály.
- Egy belső osztály, amely ugyanabban az osztályban van deklarálva, mint a helyi osztály, vagy annak egyik őse.
- Egy másik helyi osztály deklarált ugyanabban a metódusban (kódblokk).
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
}
}
Itt eltávolítottuk az PhoneNumber
osztályt a validatePhoneNumber()
metódusból, és helyi osztály helyett belső osztály lett. Ez nem akadályoz meg minket abban, hogy 2 helyi osztályunk örökölje. 2. példa — "... vagy ennek az osztálynak az őseinél." Most ez már érdekesebb. PhoneNumber
Az öröklési láncban még feljebb léphetünk . Deklaráljunk egy absztrakt AbstractPhoneNumberValidator
osztályt, amely osztályunk őse lesz 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;
}
}
}
Mint látható, nem csak deklaráltuk, hanem a PhoneNumber
belső osztályt is áthelyeztük ebbe. A leszármazottjában azonban a metódusokban deklarált lokális osztályok probléma nélkül PhoneNumberValidator
örökölhetnek !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
}
}
Az öröklődési kapcsolat miatt a leszármazott osztályon belüli helyi osztályok egy ősön belül "látják" a belső osztályokat. És végül folytassuk az utolsó csoporttal :)
Belső osztályok
Az ugyanabban a külső osztályban (vagy leszármazottjában) deklarált belső osztály örökölhet egy másik belső osztályt. Vizsgáljuk meg ezt a kerékpárokkal kapcsolatos példánkkal a belső osztályokról szóló leckében.
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
}
}
Itt deklaráltuk a Seat
belső osztályt az Bicycle
osztályon belül. Egy különleges típusú versenyülés SportSeat
örökli. De létrehozhatunk egy külön "versenykerékpár" típust, és egy külön osztályba helyezhetjük:
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!");
}
}
}
Ez is egy lehetőség. A leszármazott ( SportBicycle.SportSeat
) belső osztálya "látja" az ős belső osztályait, és örökölheti azokat. A belső osztályok öröklésének van egy nagyon fontos tulajdonsága! Az előző két példában a mi SportSeat
osztályunk belső osztály volt. De mi van, ha úgy döntünk, hogy SportSeat
egy közönséges nyilvános osztályt hozunk létre, amely egyidejűleg örökli a Seat
belső osztályt?
// 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!");
}
}
Hibát kaptunk! Kitalálod miért? :) Minden egyértelmű. Amikor a belső osztályról beszéltünk Bicycle.Seat
, megemlítettük, hogy a külső osztály egy példányára való hivatkozás implicit módon átadódik a belső osztály konstruktorának. Ez azt jelenti, hogy nem hozhat létre Seat
objektumot objektum létrehozása nélkül Bicycle
. De mi a helyzet a létrehozásával SportSeat
? Ellentétben a -val Seat
, nem rendelkezik ezzel a beépített mechanizmussal, amely implicit módon átadja a konstruktornak a külső osztály egy példányára való hivatkozást. S amíg Bicycle
objektum nélkül nem tudunk SportSeat
objektumot létrehozni, akárcsak az esetében Seat
. Ezért csak egy dolgot kell tennünk – kifejezetten át kell adni a SportSeat
konstruktornak egy objektumra való hivatkozást Bicycle
. Íme, hogyan kell csinálni:
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!");
}
}
A szuperosztály-konstruktort a Now segítségével hívjuk super();
, ha objektumot akarunk létrehozni SportSeat
, semmi sem akadályoz meg ebben:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Fú! Ez a lecke elég hosszú volt :) De sokat tanultál! Itt az ideje néhány feladat megoldásának! :)
GO TO FULL VERSION