Перегрузка Java-методом + двойная отправка
Может кто-нибудь объяснить подробно причину, по которой перегруженный метод print(Parent parent)
вызывается при работе с экземпляром Child
в моей тестовой части кода?
Существуют ли какие-либо особенности виртуальных методов или методов перегрузки/разрешения в Java?
Любая прямая ссылка на Java Lang Spec?
Какой термин описывает это поведение?
Большое спасибо.
public class InheritancePlay {
public static class Parent {
public void doJob(Worker worker) {
System.out.println("this is " + this.getClass().getName());
worker.print(this);
}
}
public static class Child extends Parent {
}
public static class Worker {
public void print(Parent parent) {
System.out.println("Why this method resolution happens?");
}
public void print(Child child) {
System.out.println("This is not called");
}
}
public static void main(String[] args) {
Child child = new Child();
Worker worker = new Worker();
child.doJob(worker);
}
}
Ответы
Ответ 1
Состояние JLS в §8.4.9 Перегрузка:
- Когда метод вызывается (§15.12), количество фактических аргументов (и любых явных аргументов типа) и типы времени компиляции аргументов используются во время компиляции для определения подпись метода, который будет вызываться (§15.12.2).
- Если метод, который должен быть вызван, является методом экземпляра, фактический метод, который будет вызываться, будет определен во время выполнения, используя динамический поиск метода (§15.12.4).
Итак, в вашем случае:
- Аргумент метода (
this
) имеет тип времени компиляции Parent
, и поэтому вызывается метод print(Parent)
.
- Если класс
Worker
был подклассифицирован, и подкласс переопределил этот метод, а экземпляр Worker
был из этого подкласса, тогда будет вызван переопределенный метод.
Двойная отправка не существует на Java. Вы должны имитировать его, например. используя шаблон посетителя. В этом шаблоне, в основном, каждый подкласс реализует метод accept
и вызывает посетителя с аргументом this
в качестве аргумента, а this
имеет подкласс типа времени компиляции, поэтому используется перегрузка требуемого метода.
Ответ 2
Причина в том, что doJob
реализована в Parent
и не перегружена в Child
. Он передает this
рабочему методу print
, потому что this
имеет тип Parent
будет вызываться метод Worker::print(Parent)
.
Чтобы иметь Worker::print(Parent)
, вам нужно перегрузить doJob
в Child
:
public static class Child extends Parent {
public void doJob(Worker worker) {
System.out.println("from Child: this is " + this.getClass().getName());
worker.print(this);
}
}
В приведенном выше коде this.getClass()
в Child
эквивалентно Child.class
.