super võtmesõna
Sissejuhatus
super võtmesõna võimaldab alamklassil viidata oma ülemklassile.
See viitab alati objekti ülemklassi osale ning võimaldab kutsuda ülemklassi konstruktoreid, meetodeid ja kasutada selle välju.
Seda võtmesõna saab kasutada kahel viisil:
- Kutsudes välja ülemklassi konstruktorit läbi
super(...)süntaksi. - Kutsudes välja ülemklassi meetodeid või välju läbi
super.methodName()võisuper.fieldNamesüntaksi.
Ülemklassi konstruktori välja kutsumine
Kui alamklassist luuakse objekt, peab ka objekti ülemklassi osa olema initsialiseeritud.
Selle saavutamiseks kutsutakse alamklassi konstruktoris välja super() käsklust ehk viidatakse ülemklassi konstruktorile, näiteks:
class Vehicle {
// Even if not part of Car class explicitly,
// these need to be initialized as well.
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; }
}
class Car extends Vehicle {
private int doors;
public Car(String brand, int speed, int doors) {
super(brand, speed); // initialises Vehicle fields
this.doors = doors;
}
}
super(brand, speed) on vastavuses Vehicle(String, int) konstruktoriga.
Java kompilaator otsib ülemklassist konstruktorit, mis ühtib antud argumentide tüübiga, sarnaselt konstruktorite aheldamisele this() käsklusega.
Sarnaselt this() käsklusega konstruktorite aheldamisele, peab ka super() käsklus olema konstruktoris esikohal:
public Car(String brand, int speed, int doors) {
this.doors = doors; // Error: call to super must be first statement
super(brand, speed);
}
Traditsiooniliselt peab super() või this() olema konstruktoris esimene käsklus.
Uuemates Java versioonides on konstruktorite reegleid paindlikumaks tehtud, mis võimaldab teatud juhtudel kirjutada ka muud koodi enne super() või this() kutsumist (JEP 513).
Kui ülemklassil on olemas ilma argumentideta konstruktor ning alamklassi konstruktor super()-it välja ei kutsuta, tehakse seda automaatselt:
class Animal {
private String type;
public Animal() { // no-arg constructor
this.type = "unknown";
}
public String getType() { return type; }
}
class Dog extends Animal {
private String name;
public Dog(String name) {
// super() is called here automatically
this.name = name;
}
}
Kui selline konstruktor puudub ülemklassis, siis peab super()-it eraldi välja kutsuma:
class Animal {
public Animal(String type) { ... } // no no-arg constructor
}
class Dog extends Animal {
public Dog(String name) {
// Java cannot insert super() automatically — compile error
}
}
Sellest hoolimata on hea tava kirjutada super() käsklus vajadusel selgesõnaliselt välja.
Ülemklassi meetodite kasutamine ja laiendamine
super võtmesõna on võimalik ka kasutada selleks, et ülemklassist meetodeid kasutada.
See tuleb eriti kasuks juhul, kui on soov ülemklassi meetodit üle kirjutada ja selle käitumist laiendada, mitte täielikult asendada.
Näiteks:
class Vehicle {
private String brand;
public Vehicle(String brand) {
this.brand = brand;
}
public String getBrand() { return brand; }
public String describe() {
return "Vehicle: " + brand;
}
}
class Car extends Vehicle {
private int doors;
public Car(String brand, int doors) {
super(brand);
this.doors = doors;
}
@Override
public String describe() {
return super.describe() + ", doors: " + doors; // extends the superclass version
}
}
Car car = new Car("Toyota", 4);
System.out.println(car.describe()); // Vehicle: Toyota, doors: 4
Ilma super.describe() kutsumata peaks ülemklassi loogikat duplikeerima, et sama tulemus saavutada:
@Override
public String describe() {
return "Vehicle: " + getBrand() + ", doors: " + doors; // duplicated logic
}
Samuti tagab see ka ühtsuse ja töökindluse juhul, kui ülemklassi loogikat muudetakse. Muudatused kanduvad automaatselt üle ka alamklassi.
Ligipääs ülemklassi väljadele
Lisaks on võimalik super võtmesõna kasutada ka selleks, et ülemklassi väljadele ligi pääseda.
Seda on ainult vaja juhul, kui ülemklassis mõni väli on protected modifikaatoriga ning mõne meetodi parameetrites on samanimeline muutuja või ülem- ja alamklassi väljade nimed ühtivad ehk ülemklassi muutujad peidetakse ära.
Näiteks:
class Vehicle {
protected String type = "vehicle";
}
class Car extends Vehicle {
protected String type = "car"; // hides Vehicle.type
public void printTypes() {
System.out.println(type); // car — Car's own field
System.out.println(super.type); // vehicle — Vehicle's field
}
}
Praktikas selline lähenemine pigem tekitab segadust ning peaks iga hinna eest vältima. Põhjusega eelistatakse kapseldamist antud olukorras.