Java. Спадковість. Перевизначення та перевантаження успадкованих методів. Поліморфізм. Динамічна диспетчеризація методів




Спадковість. Перевизначення та перевантаження успадкованих методів. Ключове слово super. Приклади. Динамічна диспетчеризація методів. Поліморфізм


Зміст


Пошук на інших ресурсах:

1. Що таке перевизначення методу?

Перевизначення методу – це випадок, коли підклас та суперклас містять методи, які мають однакове ім’я та сигнатуру типів (рисунок 1). Перевизначення методу виникає коли класи утворюють ієрархію спадковості.

Java. Спадковість. Приклад перевизначення методу method() суперкласу A в підкласі B

Рисунок 1. Приклад перевизначення методу method() суперкласу A в підкласі B

На рисунку 1 клас B успадковує (розширює) клас A. Клас A є суперкласом для класу B. У підкласі B оголошується метод з таким самим іменем як і в суперкласі A. Отже, у даному випадку, метод з іменем method() суперкласу A є перевизначеним у підкласі B.

 

2. Яка відмінність між перевизначенням та перевантаженням методу?

В ієрархії успадкування методи суперкласів та підкласів можуть бути перевизначеними та перевантаженими.



Між перевизначенням та перевантаженням методу є наступна відмінність:

  • при перевизначенні методи суперкласу та підкласу мають однакове ім’я та однакову сигнатуру типів параметрів (див. рис. 1);
  • при перевантаженні методи суперкласу та підкласу мають однакове ім’я але різні сигнатури типів параметрів. На рисунку 2 продемонстровано перевентаження методу в ієрархії успадкування.

Java. Спадковість. Приклад перевантаження методу у класах A, B, C які утворюють ієрархію

Рисунок 2. Приклад перевантаження методу з іменем method() у класах A, B, C які утворюють ієрархію

На рисунку 2 зображено 3 класи з іменами A, B, C які утворюють ієрархію успадкування. Клас B успадковує клас A. Клас C успадковує клас B. У всіх класах реалізовано різні методи, які мають однакове ім’я method(). Параметри методу в кожному класі відрізняються. Це означає, що метод method() є перевантажений.

 

3. Правило взаємодії між методами суперкласу та підкласу, які мають однакові імена та сигнатуру. Приклад

Якщо в суперкласі та підкласі реалізовано методи, що мають однакове ім’я та сигнатуру типів параметрів, то діє наступне правило:

  • метод з підкласу перевизначає метод суперкласу.

На рисунку 3 схематично зображено правило взаємодії між однойменними методами суперкласу та підкласу.

Java. Спадковість. Перевизначення методу суперкласу в методі підкласу

Рисунок 3. Демонстрація перевизначення методу суперкласу в методі підкласу

На рисунку 3 зображено приклад перевизначення методу суперкласу в підкласі. Оголошується 3 класи з іменами A, B, C. У кожному класі реалізовано метод з іменем method() який не отримує параметрів. Цей метод має однакову сигнатуру у всіх класах.

Кожен об’єкт (екземпляр) класу викликає метод класу на який він був оголошений. З екземпляру класу objB викликається метод method() який реалізований в класі B. З екземпляру класу objC викликається метод method(), що реалізований в класі C.

Нижче наведено текст програми що демонструє перевизначення методів, зображених на рисунку 3.

// суперклас
class A {
    void method() {
        // метод класу A
        System.out.println("Method A");
    }
}

// підклас класу A
class B extends A {
    void method() {
        // метод класу B
        System.out.println("Method B");
    }
}

// підклас класу B
class C extends B {
    void method() {
        // метод класу C
        System.out.println("Method C");
    }
}

public class Train01 {

    public static void main(String[] args) {
        A objA = new A();
        B objB = new B();
        C objC = new C();

        objA.method(); // викликати method() класу A
        objB.method(); // викликати method() класу B
        objC.method(); // викликати method() класу C
    }
}

Результат виконання програми

Method A
Method B
Method C

 

4. Отримання доступу з методу підкласу до методу суперкласу у випадку, якщо співпадають імена методів та сигнатури їх параметрів. Приклад

Інколи потрібно отримати доступ з методу підкласу до методу суперкласу, який має таке саме ім’я та сигнатуру параметрів. У цьому випадку використовується ключове слово super.

На рисунку 4 зображено доступ до методу суперкласу A з підкласу B. У підкласі B є метод з таким самим іменем та списком параметрів, тому, цей метод перевизначає метод суперкласу. Щоб доступитись до методу суперкласу A у методі підкласу B використовується ключове слово super.

Java. Спадковість. Виклик методу суперкласу з методу підкласу з допомогою ключового слова super

Рисунок 4. Виклик методу суперкласу з методу підкласу з допомогою ключового слова super

Текст програми, що демонструє рисунок 4 наступний

// суперклас
class A {
    void method() {
        // метод класу A
        System.out.println("Method A");
    }
}

// підклас класу A
class B extends A {
    void method() {
        // метод класу B
        System.out.println("Method B");
    }

    void method2() {
        System.out.println("Method2 - B");
        super.method(); // виклик методу суперкласу A
    }
}

public class Train01 {

    public static void main(String[] args) {

        B objB = new B(); // екземпляр класу B
        objB.method2();
    }

}

Результат виконання програми

Method2 - B
Method A

 

5. Приклад, що демонструє перевантаження методів у класах що утворюють ієрархію спадковості

Якщо класи утворюють ієрархію шляхом успадкування, то у цих класах імена методів можуть співпадати.

Метод вважається перевантаженим (не перевизначеним) у випадку, якщо:

  • в різних класах існує метод з таким самим іменем;
  • сигнатура параметрів методу у кожному класі відрізняється.

Таким чином, методи з різними сигнатурами вважаються перевантаженими а не перевизначеними.

На рисунку 5 зображено приклад перевантаження методу з іменем method(). Оголошуються три класи з іменами A, B, C які утворюють ієрархію. У класі A метод з іменем method() реалізований без параметрів. У класі B метод з іменем method() реалізований з одним параметром типу int. У класі C метод з іменем method() реалізований з одним параметром типу double.

Java. Спадковість. Перевантаження методу з іменем method() у класах, що утворюють ієрархію

Рисунок 5. Перевантаження методу з іменем method() у класах, що утворюють ієрархію

Текст програми, що демонструє рисунок 5 наступний

// суперклас
class A {

    void method() {
        // метод класу A
        System.out.println("Class A. Method without parameters.");
    }
}

// підклас класу A
class B extends A {

    void method(int t) {
        // метод класу B
        System.out.println("Class B. Method with 1 parameter of type int: "+t);
    }
}

// підклас класу B
class C extends B {

    void method(double x) {
        // метод класу C
        System.out.println("Class C. Method with 1 parameter of type double: "+x);
    }
}

public class Train01 {

    public static void main(String[] args) {
        C objC = new C(); // екземпляр класу C

        objC.method(); // виклик методу без параметрів класу A
        objC.method(5); // виклик методу класу B
        objC.method(9.75); // виклик методу класу C
    }
}

Результат роботи програми

Class A. Method without parameters.
Class B. Method with 1 parameter of type int: 5
Class C. Method with 1 parameter of type double: 9.75





 

6. Що таке динамічна диспетчеризація методів? Приклад реалізації поліморфізму в Java

Динамічна диспетчеризація методів є один з найбільш ефективних принципів об’єктно-орієнтованого програмування.

Динамічна диспетчеризація методів – це спеціальний механізм, який дозволяє викликати перевизначений метод в процесі виконання програми а не під час компіляції. Динамічна диспетчеризація методів важлива при реалізації поліморфізму.

Рисунок 6 демонструє динамічну диспетчеризацію методів. Реалізовано 3 класи з іменами A, B, C які утворюють ієрархію. У класах реалізовано метод з іменем method(). Сигнатура методу у всіх класах однакова.

Java. Спадковість. Динамічна диспетчеризація методів для трьох класів, що утворюють ієрархію

Рисунок 6. Демонстрація динамічної диспетчеризації методів для трьох класів, що утворюють ієрархію

На початку коду демонстрації (рисунок 6) оголошується посилання refA на базовий клас A

A refA;

Після оголошення цьому посиланню можна присвоювати значення посилання на підклас класу A.

Таким чином, з допомогою посилання refA можна викликати методи класів A, B, C що утворюють ієрархію. Якщо refA посилається на екземпляр класу A, то рядок

refA.method();

буде викликати метод з іменем method() класу A.

Аналогічно, якщо посилання refA посилається на екземпляр (об’єкт) класу B, то

refA.method();

буде викликати відповідний метод класу B.

Те саме стосується і класу C.

Як видно з рисунку 6, відповідний варіант методу method() визначається типом об’єкту, на який посилається посилання refA, а не типом цього посилання в момент його оголошення.

Нижче наведено програмний код, що демонструє динамічну диспетчеризацію методів, зображену на рисунку 6.

// суперклас
class A {
    void method() {
        // метод класу A
        System.out.println("Class A");
    }
}

// підклас класу A
class B extends A {
    void method() {
        // метод класу B
        System.out.println("Class B");
    }
}

// підклас класу B
class C extends B {
    void method() {
        // метод класу C
        System.out.println("Class C");
    }
}

public class Train01 {

    public static void main(String[] args) {
        // екземпляри класів A, B, C
        A objA = new A();
        B objB = new B();
        C objC = new C();

        // посилання на суперклас A
        A refA; // посиланню refA можна присвоїти objA, objB, objC

        refA = objA; // refA посилається на екземпляр класу A
        refA.method(); // виклик методу method() класу A

        //
        refA = objB; // refA -> objB
        refA.method(); // objB.method()

        //
        refA = objC; // refA -> objC
        refA.method(); // objC.method()
    }
}

Результат виконання програми

Class A
Class B
Class C

 

7. Приклад, що демонструє способи реалізації поліморфізму (динамічної диспетчеризації методів)

Поліморфізм може бути реалізований у програмах двома способами:

  • при присвоєнні (=) посиланню на базовий клас екземпляру будь-якого похідного класу. У цьому випадку з допомогою посилання (через символ ‘.‘) викликається перевизначений метод того екземпляру, на який на даний момент вказує це посилання;
  • при реалізації деякого методу, який отримує параметром посилання на базовий клас. У цьому випадку, в тілі методу викликається перевизначений метод (метод, що має спільне ім’я та сигнатуру в усіх класах ієрархії) за посиланням. Від того, на екземпляр якого класу вказує посилання, залежить який саме перевизначений метод буде викликано.

Нижченаведений приклад демонструє ці два випадки на прикладі ієрархії з трьох класів A, B, C. Усі класи мають метод Print() без параметрів.

// Деяка ієрархія класів.
// Всі класи мають метод Print().
class A {
  public void Print() {
    System.out.println("A.Print()");
  }
}

class B extends A {
  public void Print() {
    System.out.println("B.Print()");
  }   
}

class C extends B {
  public void Print() {
    System.out.println("C.Print()");
  }
}

public class TrainPoymorphism {
  // Метод, що отримує посилання на базовий клас A
  public static void CallPrint(A ref) {
    // за посиланням викликати метод Print() деякого класу з ієрархії
    ref.Print();
  }

  public static void main(String[] args) {
    // Демонстрація поліморфізму на прикладі передачі
    // параметру в метод CallPrint()

    // 1. Створити екземпляри класів A, B, C
    A objA = new A();
    B objB = new B();
    C objC = new C();

    // 2. Створити посилання на базовий клас в ієрархії A <- B <- C
    A refA;

    // 3. Демонстрація поліморфізму
    // 3.1. Демонстрація поліморфізму через присвоєння =
    refA = objA;  // refA -> objA
    refA.Print(); // A.Print()

    refA = objB;  // refA -> objB
    refA.Print(); // B.Print()

    refA = objC;  // refA -> objC
    refA.Print(); // C.Print()

    System.out.println("-------------------");

    // 3.2. Демонстрація поліморфізму через передачу
    //     посилання на базовий клас в метод CallPrint(A ref)
    refA = objA;     // refA -> objA
    CallPrint(refA); // A.Print()

    refA = objB;     // refA -> objB
    CallPrint(refA); // B.Print()

    refA = objC;     // refA -> objC
    CallPrint(refA); // C.Print()       
  }
}

Результат виконання програми

A.Print()
B.Print()
C.Print()
-------------------
A.Print()
B.Print()
C.Print()

 


Зв’язані теми