JavaRush /Курси /JAVA 25 SELF /Використання super: виклик конструктора й методів батьків...

Використання super: виклик конструктора й методів батьківського класу

JAVA 25 SELF
Рівень 17 , Лекція 2
Відкрита

1. Використання super для виклику методів батьківського класу

Коли ви створюєте підклас, іноді потрібно звернутися до полів або методів батьківського класу, особливо якщо ви їх перевизначили або «затінили» (сховали) у підкласі. Для цього в Java є спеціальне ключове слово — super.

Можна уявити, що super — це пряме звернення по допомогу до батьківського класу, коли в підкласі потрібно явно використати те, що визначено у батьківському класі.

Уявіть, що у вас є клас Animal із методом eat(), який просто виводить "Тварина їсть". А в класі Cat ви хочете, щоб кішка спочатку робила щось своє (наприклад, нявкала), а потім усе одно виконувала стандартне «батьківське». Ось тут і стане у пригоді super.eat().

Коли в підкласі ви перевизначаєте метод, але хочете всередині нього все одно викликати реалізацію цього методу з батьківського класу, використовуйте super.ім'яМетоду().

Приклад: розширюємо поведінку

class Animal {
    void eat() {
        System.out.println("Тварина їсть");
    }
}

class Cat extends Animal {
    @Override
    void eat() {
        System.out.println("Кішка нюхає їжу...");
        super.eat(); // викликаємо метод eat() з Animal
        System.out.println("Кішка задоволено муркоче");
    }
}

Як це працює?

  • Коли у об’єкта типу Cat викликають метод eat(), спочатку виконується код із Cat.eat(), тобто власний метод.
  • Усередині цього методу ми явно викликаємо super.eat(), тобто реалізацію з батьківського класу Animal.
  • Це дає змогу додати додаткову поведінку, не забувши про «батьківське».

Практика: використовуємо у застосунку

Припустімо, у нашому навчальному застосунку є базовий клас Animal і підкласи Dog і Cat. Ми хочемо, щоб під час годування тварини виконувалися як загальні дії (наприклад, збільшення ситості), так і специфічні для кожної тварини.

class Animal {
    int satiety = 0;

    void eat() {
        satiety += 10;
        System.out.println("Тварина їсть. Ситість: " + satiety);
    }
}

class Dog extends Animal {
    @Override
    void eat() {
        System.out.println("Собака виляє хвостом перед їжею");
        super.eat();
    }
}

Тепер, якщо викликати dog.eat(), ви побачите обидва повідомлення, і satiety збільшиться коректно.

2. Використання super для доступу до полів батьківського класу

Якщо в підкласі ви оголосили поле з таким самим ім’ям, як і в батьківському класі, воно «затінює» поле батьківського класу. Іноді потрібно отримати доступ до оригінального поля з батьківського класу — для цього й знадобиться super.ім'яПоля.

Приклад: затінення поля

class Animal {
    String name = "Тварина";
}

class Cat extends Animal {
    String name = "Кішка";

    void printNames() {
        System.out.println("Імʼя з Cat: " + name);
        System.out.println("Імʼя з Animal: " + super.name);
    }
}

Виклик new Cat().printNames(); виведе:

Імʼя з Cat: Кішка
Імʼя з Animal: Тварина

На практиці «затінювати» поля без крайньої потреби не варто, але про таку можливість слід знати.

3. Виклик конструктора батьківського класу через super(...)

Як створюються об’єкти в ієрархії?

Коли ви створюєте об’єкт підкласу, спочатку викликається конструктор батьківського класу, а вже потім — конструктор підкласу. Це потрібно, щоб усі поля ініціалізувалися правильно, оскільки підклас «успадковує» частину стану від батьківського класу.

Явний виклик конструктора батьківського класу

Якщо у батьківського класу є конструктор без параметрів — усе просто: Java викличе його автоматично перед виконанням конструктора підкласу. Але якщо в батьківського класу немає конструктора без параметрів, ви зобов’язані явно викликати потрібний конструктор через super(...).

Приклад:

class Animal {
    String name;

    Animal(String name) {
        this.name = name;
        System.out.println("Створено тварину: " + name);
    }
}

class Cat extends Animal {
    Cat(String name) {
        super(name); // обовʼязково! Немає конструктора Animal() без параметрів
        System.out.println("Створено кішку: " + name);
    }
}

Виклик new Cat("Мурка") надрукує:

Створено тварину: Мурка
Створено кішку: Мурка

Важливо: Виклик конструктора батьківського класу через super(...) має бути першим рядком конструктора підкласу. Спроба зробити щось до цього виклику призведе до помилки компіляції.

Якщо не викликати явно?
Якщо в батьківського класу є лише конструктор із параметрами, а ви не викликали його явно через super(...), компілятор видасть помилку: "constructor Animal in class Animal cannot be applied to given types".

4. Корисні нюанси

Коли використовувати super?

Для розширення, а не заміни поведінки.
Іноді ви хочете не повністю замінити поведінку методу, а лише «розширити» її. Наприклад, додати щось до або після батьківської логіки. У таких випадках використовуйте super.ім'яМетоду() у тілі перевизначеного методу.

Для ініціалізації успадкованих полів.
Якщо в батьківського класу є обов’язкові для ініціалізації поля (наприклад, ім’я тварини), обов’язково викликайте конструктор батьківського класу з потрібними параметрами через super(...).

Для доступу до прихованих полів/методів.
Якщо ви з якихось причин «затінили» поле або метод батьківського класу, і вам усе ж потрібно до нього звернутися, використовуйте super.ім'яПоля або super.ім'яМетоду().

Обмеження та особливості використання super

  • Виклик конструктора батьківського класу через super(...) можна робити лише в конструкторі і тільки першим рядком.
  • Не можна викликати конструктор батьківського класу поза конструктором підкласу.
  • Якщо не викликати super(...) явно, Java спробує викликати конструктор без параметрів батьківського класу (якщо він є).
  • Ключове слово super не можна використовувати в статичних методах — лише в нестатичних (екземплярних) методах і конструкторах.
  • Якщо метод або поле батьківського класу private, то super не допоможе: доступу до приватних членів немає.

5. Приклади для закріплення

Приклад 1. Розширюємо метод за допомогою super

class Animal {
    void makeSound() {
        System.out.println("Тварина видає звук");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        super.makeSound(); // спочатку робимо стандартну дію
        System.out.println("Собака гавкає: Гав-гав!");
    }
}

Приклад 2. Виклик конструктора батьківського класу

class Vehicle {
    String brand;

    Vehicle(String brand) {
        this.brand = brand;
        System.out.println("Транспорт: " + brand);
    }
}

class Car extends Vehicle {
    int year;

    Car(String brand, int year) {
        super(brand); // викликаємо конструктор батьківського класу
        this.year = year;
        System.out.println("Автомобіль " + brand + ", рік: " + year);
    }
}
Car car = new Car("Toyota", 2023);
// Вивід:
// Транспорт: Toyota
// Автомобіль Toyota, рік: 2023

Приклад 3. Класична помилка: забули викликати super(...)

class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }
}

class Cat extends Animal {
    Cat() {
        // super(); // Помилка! Немає конструктора Animal() без параметрів
        // Потрібно явно викликати super(name)
        super("Безіменна кішка");
    }
}

6. Типові помилки під час роботи з super

Помилка № 1: Виклик super(...) не першим рядком конструктора.
Java суворо вимагає, щоб виклик конструктора батьківського класу через super(...) був першим рядком конструктора підкласу. Якщо ви спробуєте зробити щось до цього виклику (наприклад, вивести повідомлення), компілятор видасть помилку.

Помилка № 2: Немає відповідного конструктора в батьківському класі.
Якщо у батьківського класу немає конструктора без параметрів, а ви не викликали інший конструктор через super(...), компілятор не зможе згенерувати виклик за замовчуванням і повідомить про помилку.

Помилка № 3: Спроба звернутися до приватних членів батьківського класу через super.
Ключове слово super не надає магічного доступу до приватних полів або методів батьківського класу. Якщо щось оголошено як private, воно залишається недоступним для підкласу.

Помилка № 4: Затінення полів і методів без розуміння.
Якщо ви оголосили в підкласі поле або метод із тим самим ім’ям, що й у батьківського класу, і забули про це, можливі неочікувані результати. Завжди пам’ятайте, що в такому разі доступ до батьківського члена — лише через super.

Помилка № 5: Використання super у статичному методі.
У статичних методах не можна використовувати super, оскільки вони не належать конкретному об’єкту.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ