Alamklass
Sissejuhatus
Eelmises peatükis käsitlesime pärilikkust ja extends võtmesõna.
Klassi, mis on tuletatud teisest klassist, nimetatakse alamklassiks.
Klassi, millest alamklass tuleneb, nimetatakse ülemklassiks.
Kõikidel klassidel peale Object-tüüpi klassi on olemas oma üks ja ainus ülemklass.
Javas on kõik klassid üht- või teistpidi tuletatud Object klassist:
Seega, kui pöördutakse mõne meetodi poole someObject.someMethod(), siis otsitakse seda kõigepealt someObject instantsist.
Kui seal ei ole, siis selle ülemklassis jne.
Kuni Object klassini välja.
Alamklass Javas pärib omadusi ülemklassist. Tänu pärimisele lisavad alamklassid spetsiifilist käitumist üldistatud ülemklassile.
Selles peatükis tutvume mida täpsemalt on võimalik alamklassidega korda saata ning tutvume lähemalt konstruktorite ja protected nähtavuse modifikaatoriga.
Väljade ja meetodite lisamine
Alamklass pärib ülemklassilt kõik talle nähtavad omadused. Lisaks sellele on võimalik alamklassile omadusi juurde lisada.
Vaatleme Vehicle ülemklassi ja Car alamklassi.
Kuna Vehicle on üldistus, siis see väga palju spetsiifikat ei oma.
Car klass omakorda võib juurde lisada näiteks infot selle kohta, et mitu ust autol on ning kuidas signaali laskmine võiks töötada:
class Vehicle {
private String brand;
private int speed;
public Vehicle(String brand, int speed) {
this.brand = brand;
this.speed = speed;
}
public String getBrand() { return brand; }
public int getSpeed() { return speed; }
public void drive() {
System.out.println(brand + " is driving");
}
}
class Car extends Vehicle {
private int doors; // new field, specific to Car
public Car(String brand, int speed, int doors) {
super(brand, speed); // initialises the Vehicle part
this.doors = doors; // initialises the Car-specific part
}
public int getDoors() { return doors; } // new method, specific to Car
public void honk() {
System.out.println(getBrand() + " beeps!");
}
}
Car car = new Car("Toyota", 120, 4);
car.drive(); // Toyota is driving — inherited from Vehicle
System.out.println(car.getBrand()); // Toyota — inherited from Vehicle
System.out.println(car.getDoors()); // 4 — defined in Car
car.honk(); // Toyota beeps! — defined in Car
Car klassil on juurdepääs kõikidele Vehicle klassi avalikele meetoditele justkui need kuuluksid talle endale.
Alamklassi konstruktorid
Meeldetuletuseks saate konstruktorite kohta lugeda siit.
Alamklassid konstruktoreid ei päri, iga alamklass peab ise endale konstruktori defineerima.
Kui ülemklassil pole null-argumendilist konstruktorit (nt: public Vehicle() {}), siis alamklassi konstruktor peab esimese asjana välja kutsuma ka super(...) meetodit.
Antud viisil saavad ülemklassi väljad oma väärtused kätte:
class Car extends Vehicle {
private int doors;
public Car(String brand, int speed, int doors) {
super(brand, speed); // must be the first line
this.doors = doors;
}
}
Ilma super võtmesõnata tekiks kompileerimisviga:
class Car extends Vehicle {
private int doors;
public Car(String brand, int speed, int doors) {
// super(brand, speed) is missing — compile error:
// "There is no default constructor available in Vehicle"
this.doors = doors;
}
}
super märksõna kohta saab rohkem lugeda siit.
protected nähtavus
Meeldetuletuseks saate nähtavuse modifikaatorite kohta lugeda siit.
Eelmises peatükis sai mainitud, et private väljad ei ole alamklassides otseselt kättesaadavad.
private ja public nähtavuse vahel on olemas modifikaator, mis võimaldab ligipääsu ülemklassi meetoditele ja väljadele.
Selleks kasutatakse protected nähtavuse modifikaatorit.
Tuletame meelde, mis vahe on erinevatel modifikaatoritel:
| Modifikaator | Ligipääs samast klassist | Samast pakist | Alamklassist | Kõikjalt |
|---|---|---|---|---|
private | + | - | - | - |
| (none) | + | + | - | - |
protected | + | + | + | - |
public | + | + | + | + |
Ehk protected võimaldab meetoditele ja väljadele ligi pääseda:
- samast klassist
- antud klassi alamklassidest
- samast pakist
Näiteks:
class Vehicle {
protected String brand; // accessible in subclasses
private int speed; // not accessible in subclasses
public Vehicle(String brand, int speed) {
this.brand = brand;
this.speed = speed;
}
public int getSpeed() { return speed; }
}
class Car extends Vehicle {
public Car(String brand, int speed) {
super(brand, speed);
}
public void printInfo() {
System.out.println(brand); // OK — protected
// System.out.println(speed); // Error — private
System.out.println(getSpeed()); // OK — public getter
}
}
Praktikas on siiski tavaline hoida ülemklassi väljad privaatsena ning teha need kättesaadavaks läbi public või protected getter meetodite.
Kuigi protected võimaldab alamklassi välju otseselt kasutada, peetakse tavaliselt paremaks kapseldamist, et hoida klasside sisemine struktuur paindlikuna ja vältida tugevat omavahelist sõltuvust.
Ülemklassi tüübi kasutamine
Alamklassi objekti saab kasutada igal pool, kus oodatakse ülemklassi tüüpi.
Kuna Car on Vehicle tüüpi, saab seda hoida Vehicle tüüpi muutujas:
Vehicle v = new Car("Toyota", 120, 4);
v.drive(); // Toyota is driving
System.out.println(v.getBrand()); // Toyota
Antud juhul on muutuja v kaudu kättesaadavad ainult Vehicle klassis defineeritud meetodid, kuigi tegelik objekt on Car tüüpi:
Vehicle v = new Car("Toyota", 120, 4);
// v.honk(); // Error — honk() is not part of Vehicle
// v.getDoors(); // Error — getDoors() is not part of Vehicle
Car klassi meetodite kasutamiseks peab Vehicle tüübi asendama Car tüübiga või kasutama tüübiteisedust:
Vehicle v = new Car("Toyota", 120, 4);
Car car = (Car) v; // Cast Vehicle to Car
car.honk(); // Toyota beeps!
// or
((Car) v).honk(); // Inline casting
Kuigi tüübiteisendust on juba eelnevalt käsitletud (vastav peatükk), siis pärilikkus laiendab antud teemat. Tüübiteisendusest pärilikkuse taustal saate lugeda siit.