Не могу понять полиморфный вызов метода

434
09 августа 2017, 18:36

У меня есть класс Pair:

public class Pair {
   public void getObject(Object o){
       System.out.println("Text from Pair");}
 }

От него наследуется класс Detail, у которого есть такой же метод, но он принимает объект другого типа:

public class Detail extends Pair {
  public void getObject(Date o){
    System.out.println("Text from Detail");}
}

В Main у меня вот это:

Detail d = new Detail();
Pair p = d;
p.getObject(new Date());

И по сути должен исполняться полиморфный вызов метода, но для объекта Detail вызывается метод getObject с класса Pair, почему так? Как именно здесь работает вызов метода?

Answer 1

Для того, чтобы функция производного класса считалась полиморфным вариантом функции базового класса, у них должны совпадать не только имена, но и типы аргументов. В вашем случае две функции считаются всего лишь перегрузкой имени getObject. И в p.getObject(new Date()); происходит вызов класса Pair.

похожий вопрос

Answer 2

В java есть 3 поведения, относящиеся к Вашей теме:

  • overriding (официальная документация здесь)
  • overloading (официальная документация здесь - секция "Overloading Methods")
  • hiding (официальная документация здесь)

Поскольку в документации сказано, что:

The overriding method has the same name, number and type of parameters, and return type as the method that it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This subtype is called a covariant return type.

и в Вашем случае у Вас нарушено правило number and type of parameters, то получается это не override. В то же время, касательно overload написано следующее:

The Java programming language supports overloading methods, and Java can distinguish between methods with different method signatures. This means that methods within a class can have the same name if they have different parameter lists (there are some qualifications to this that will be discussed in the lesson titled "Interfaces and Inheritance").

Что относится именно к Вашей ситуации. Таким образом, вызывая метод по ссылке Pair p Вы будете вызывать метод класса Pair, не смотря на то, что фактически это объект Detail.

Кроме того, в java есть механизм отслеживания подобных недоразумений в виде @Override аннотации, пометив ей метод компилятор выдаст ошибку в случае, если это не override. В Вашем случае:

@Override
public void getObject(Date o){
    System.out.println("Text from Detail");}

Компилятор выдаст ошибку "Method doesn't override method from its superclass".

Ну и в конце концов, чтобы добиться настоящего overrid'a в классе Detail метод должен выглядеть следующим образом:

public void getObject(Object o){
    System.out.println("Text from Detail");}

В таком случае Вы получите ожидаемый результат.

Надеюсь удалось подробно все объяснить. Удачи в дальнейшем изучении =).

Answer 3

Что бы понять что происходит нужно уточнить несколько понятий:

  1. Перезрузка. Вы можете иметь в одном классе 2 метода с одинаковыми именем но разными параметрами.

  2. Переопределение. Необходимо полное совпадение типов входящих параметров.

  3. Наследование. Предоставляет поля и методы родительского класса

Это не определяния понятий перегрузка, переопределение и наследование а только некоторые моменты имеющие отношение к вопросу.

В вашем случае объект Detail имеет два варианта метода getObject, один тот который вы определили явно прямо в классе : public void getObject(Date o), и второй который он получил от родителя в результате наследования : public void getObject(Object o).

Когда VM нужно определить какой метод вызывать, она смотрит какого типа параметр вы отправили в метод, и по типу этого параметра выбирает подходищий выриант перегрузки.

Следуя этой логике можно подумать что должен быть вызван метод с параметром типа Date. Но вы делайте приведение типов:

Detail d = new Detail();
Pair p = d; //Вот здесь. Это равнозначно Pair p = (Pair) d;
p.getObject(new Date());

А список методов для объекта всегда определяется по типу указателя. И так как тип указателя у вас теперь Pair а у него есть только один вариант этого метода то он и вызвался. Ну а так как он принимает Object то и Date "заглотил не морщась". И вызвался единственный имеющийся у Pair метод getObject(Object).

READ ALSO
Как считать написанный текст через exec

Как считать написанный текст через exec

Добрый вечер! Через Runtimeexec вызывается определенная Linux-программа, которая динамически отображает результат вот в таком виде (числа меняются...

276
IntelliJ IDEA показывает “характер”

IntelliJ IDEA показывает “характер”

Как-то пришлось перезагрузить зависший компВ итоге IntelliJ самостоятельно поменяла тему оформления и начала сыпать ошибками

301
Генерация токена для Apple Music на java

Генерация токена для Apple Music на java

При генерации токена получаю ошибку:

328