1. Użycie super do wywołania metod klasy bazowej
Gdy tworzysz podklasę, czasem pojawia się sytuacja, w której trzeba odwołać się do pól lub metod klasy bazowej, zwłaszcza jeśli zostały one nadpisane lub „przesłonięte” w podklasie. Do tego w Javie istnieje specjalne słowo kluczowe — super.
Jeśli szukać analogii, to super — to jak „mamo, pomóż!”, kiedy w podklasie chcesz jawnie odwołać się do tego, co zdefiniowano w rodzicu.
Wyobraź sobie, że masz klasę Animal z metodą eat(), która po prostu wypisuje "Zwierzę je". A w klasie Cat chcesz, aby kot najpierw zrobił coś swojego (na przykład zamiauczał), a potem i tak wykonał standardowe „zwierzę je”. Tu właśnie przydaje się super.eat().
Gdy w podklasie nadpisujesz metodę, ale chcesz w jej wnętrzu mimo wszystko wywołać implementację tej metody z klasy bazowej, użyj super.nazwaMetody().
Przykład: rozszerzamy zachowanie
class Animal {
void eat() {
System.out.println("Zwierzę je");
}
}
class Cat extends Animal {
@Override
void eat() {
System.out.println("Kot wącha jedzenie...");
super.eat(); // wywołujemy metodę eat() z Animal
System.out.println("Kot z zadowoleniem mruczy");
}
}
Jak to działa?
- Gdy wywoływana jest eat() na obiekcie typu Cat, najpierw wykonuje się kod z Cat.eat(), czyli metoda własna.
- Wewnątrz tej metody jawnie wywołujemy super.eat(), czyli implementację z klasy rodzica Animal.
- Pozwala to dodać dodatkowe zachowanie, nie zapominając o „rodzicielskim”.
Praktyka: użycie w aplikacji
Załóżmy, że w naszej aplikacji edukacyjnej jest klasa bazowa Animal oraz podklasy Dog i Cat. Chcemy, aby podczas karmienia zwierzęcia wykonywały się zarówno działania wspólne (np. zwiększenie sytości), jak i specyficzne dla każdego zwierzęcia.
class Animal {
int satiety = 0;
void eat() {
satiety += 10;
System.out.println("Zwierzę je. Sytość: " + satiety);
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("Pies merda ogonem przed jedzeniem");
super.eat();
}
}
Teraz, jeśli wywołasz dog.eat(), zobaczysz oba komunikaty, a satiety zwiększy się poprawnie.
2. Użycie super do dostępu do pól klasy bazowej
Jeśli w podklasie zadeklarujesz pole o tej samej nazwie, co w klasie rodzica, to „przesłania” ono pole rodzica. Czasem trzeba uzyskać dostęp do oryginalnego pola z klasy bazowej — do tego służy super.nazwaPola.
Przykład: przesłanianie pola
class Animal {
String name = "Zwierzę";
}
class Cat extends Animal {
String name = "Kot";
void printNames() {
System.out.println("Imię z Cat: " + name);
System.out.println("Imię z Animal: " + super.name);
}
}
Wywołanie new Cat().printNames(); wypisze:
Imię z Cat: Kot
Imię z Animal: Zwierzę
W praktyce „przesłanianie” pól nie jest zalecane bez wyraźnej potrzeby, ale warto znać taką możliwość.
3. Wywołanie konstruktora klasy bazowej przez super(...)
Jak tworzą się obiekty w hierarchii?
Gdy tworzysz obiekt podklasy, najpierw wywoływany jest konstruktor klasy bazowej, a dopiero potem — konstruktor podklasy. Jest to potrzebne, aby wszystkie pola zostały poprawnie zainicjalizowane, ponieważ podklasa „dziedziczy” część stanu od rodzica.
Jawne wywołanie konstruktora klasy bazowej
Jeśli klasa bazowa ma konstruktor bez parametrów — wszystko proste: Java sama wywoła go przed wykonaniem konstruktora podklasy. Ale jeśli rodzic nie ma konstruktora bez parametrów, musisz jawnie wywołać właściwy konstruktor przez super(...).
Przykład:
class Animal {
String name;
Animal(String name) {
this.name = name;
System.out.println("Utworzono zwierzę: " + name);
}
}
class Cat extends Animal {
Cat(String name) {
super(name); // koniecznie! Brak konstruktora Animal() bez parametrów
System.out.println("Utworzono kota: " + name);
}
}
Wywołanie new Cat("Murka") wydrukuje:
Utworzono zwierzę: Murka
Utworzono kota: Murka
Ważne: Wywołanie konstruktora rodzica przez super(...) musi być pierwszą linią konstruktora podklasy. Jeśli spróbujesz napisać coś przed tym wywołaniem, kompilator będzie niezadowolony i ci o tym przypomni.
A jeśli nie wywołasz jawnie?
Jeśli rodzic ma tylko konstruktor z parametrami, a ty nie wywołasz go jawnie przez super(...), kompilator zgłosi błąd: "constructor Animal in class Animal cannot be applied to given types".
4. Przydatne niuanse
Kiedy używać super?
Do rozszerzania, a nie zastępowania zachowania.
Czasem nie chcesz całkowicie zastąpić zachowania metody, a jedynie je „rozszerzyć”. Na przykład dodać coś przed lub po logice rodzica. W takich przypadkach użyj super.nazwaMetody() w ciele nadpisanej metody.
Do inicjalizowania odziedziczonych pól.
Jeśli rodzic ma pola wymagające inicjalizacji (np. imię zwierzęcia), koniecznie wywołaj konstruktor rodzica z odpowiednimi parametrami przez super(...).
Do dostępu do ukrytych pól/metod.
Jeśli z jakiegoś powodu „przesłoniłeś” pole lub metodę rodzica i jednak musisz się do nich odwołać, użyj super.nazwaPola lub super.nazwaMetody().
Ograniczenia i cechy użycia super
- Wywołanie konstruktora rodzica przez super(...) można robić tylko w konstruktorze i tylko w pierwszej linii.
- Nie można wywołać konstruktora rodzica poza konstruktorem podklasy.
- Jeśli nie wywołasz super(...) jawnie, Java spróbuje wywołać konstruktor bez parametrów rodzica (jeśli istnieje).
- Słowa kluczowego super nie można używać w metodach statycznych — tylko w metodach niestatycznych (instancyjnych) i konstruktorach.
- Jeśli metoda lub pole rodzica ma modyfikator private, to super nie pomoże: do prywatnych składowych nie ma dostępu.
5. Przykłady do utrwalenia
Przykład 1. Rozszerzamy metodę przy użyciu super
class Animal {
void makeSound() {
System.out.println("Zwierzę wydaje dźwięk");
}
}
class Dog extends Animal {
@Override
void makeSound() {
super.makeSound(); // najpierw wykonujemy działanie standardowe
System.out.println("Pies szczeka: Hau-hau!");
}
}
Przykład 2. Wywołanie konstruktora klasy bazowej
class Vehicle {
String brand;
Vehicle(String brand) {
this.brand = brand;
System.out.println("Pojazd: " + brand);
}
}
class Car extends Vehicle {
int year;
Car(String brand, int year) {
super(brand); // wywołujemy konstruktor rodzica
this.year = year;
System.out.println("Samochód " + brand + ", rok: " + year);
}
}
Car car = new Car("Toyota", 2023);
// Wyjście:
// Pojazd: Toyota
// Samochód Toyota, rok: 2023
Przykład 3. Klasyczny błąd: zapomniano wywołać super(...)
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
Cat() {
// super(); // Błąd! Brak konstruktora Animal() bez parametrów
// Trzeba jawnie wywołać super(name)
super("Bezimienna kotka");
}
}
6. Typowe błędy podczas pracy z super
Błąd nr 1: Wywołanie super(...) nie w pierwszej linii konstruktora.
Java surowo wymaga, aby wywołanie konstruktora rodzica przez super(...) było pierwszą linią konstruktora podklasy. Jeśli spróbujesz zrobić coś przed tym wywołaniem (np. wypisać komunikat), kompilator zgłosi błąd.
Błąd nr 2: Brak odpowiedniego konstruktora u rodzica.
Jeśli klasa bazowa nie ma konstruktora bez parametrów, a nie wywołałeś innego konstruktora przez super(...), kompilator nie będzie mógł wygenerować domyślnego wywołania i zgłosi błąd.
Błąd nr 3: Próba dostępu do prywatnych składowych rodzica przez super.
Słowo kluczowe super nie daje magicznego dostępu do prywatnych pól lub metod rodzica. Jeśli coś jest zadeklarowane jako private, pozostaje niedostępne dla podklasy.
Błąd nr 4: Przesłanianie pól i metod bez zrozumienia.
Jeśli w podklasie zadeklarujesz pole lub metodę o tej samej nazwie, co u rodzica, i o tym zapomnisz, możliwe są nieoczekiwane rezultaty. Zawsze pamiętaj, że w takim przypadku dostęp do składowej rodzica — tylko przez super.
Błąd nr 5: Używanie super w metodzie statycznej.
W metodach statycznych nie można używać super, ponieważ nie należą one do konkretnego obiektu.
GO TO FULL VERSION