Java. Наследование. Переопределение и перегрузка унаследованных методов. Полиморфизм. Динамическая диспетчеризация методов




Наследование. Переопределение и перегрузка унаследованных методов. Ключевое слово super. Примеры. Динамическая диспетчеризация методов. Полиморфизм


Содержание


Поиск на других ресурсах:

1. Что означает переопределение метода?

Переопределение метода – это случай, когда подкласс и суперкласс содержат методы, которые имеют одинаковое имя и сигнатуру типов (рисунок 1). Переопределение метода возникает в случаях, когда классы образовывают иерархию наследования.

Java. Наследование. Пример переопределения метода суперкласса 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();

        obj.method(); // вызвать method() класса A
        obj.method(); // вызвать method() класса B
        obj.method(); // вызвать method() класса C
    }
}

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

Method A
Method B
Method C

 

4. Получение доступа из метода подкласса к методу суперкласса в случае, когда совпадают имена методов и сигнатуры их параметров. Ключевое слово super. Пример

Иногда нужно получить доступ из метода подкласса к методу суперкласса, который имеет такое же имя и сигнатуру параметров. В этом случае используется ключевое слово 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, переопределяет метод класса A
        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 obj = new B(); // экземпляр класса B
        objB.method2();
    }
}

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

Method2 - B
Method A

 

5. Пример, который демонстрирует перегрузку методов в классах, которые образовывают иерархию наследования

Если классы образовывают иерархию путем наследования, то в этих классах имена методов могут совпадать.

Метод считается перегруженным (не переопределенным) в случае, если:

  • в разных классах существует метод с таким же именем;
  • сигнатура параметров метода в каждом классе отличается.

Таким образом, методы с разными сигнатурами считаются перегруженными а не переопределенными.

На рисунке 5 изображен пример перегрузки метода с именем method(). Объявляются три класса с именами A, B, C которые образовывают иерархию. В классе A метод с именем method() реализован без параметров. В классе B метод с именем method() реализован с одним параметром типа int. В классе C метод с именем method() реализован с одним параметром типа double.

Java. Наследование. Перегрузка метода в классах, которые образовывают иерархию

Рисунок 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 obj = new C(); // экземпляр класса C

        obj.method(); // вызов метода без параметров класса A
        obj.method(5); // вызов метода класса B
        obj.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()

 


Связанные темы