Перегрузка метода 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);
}
}
2 ответа
Состояния JLS в §8.4.9 Перегрузка:
- Когда метод вызывается (§15.12), число фактических аргументов (и любых явных аргументов типа) и типы аргументов во время компиляции используются во время компиляции, чтобы определить сигнатуру метода, который будет вызван (§15.12.2).
- Если метод, который должен быть вызван, является методом экземпляра, фактический метод, который должен быть вызван, будет определен во время выполнения, используя динамический поиск метода (§15.12.4).
Итак, в вашем случае:
- Аргумент метода (
this
) имеет тип времени компиляцииParent
и так методprint(Parent)
вызывается. - Если
Worker
класс был разделен на подклассы, и подкласс переопределил бы этот метод, иworker
экземпляр принадлежит этому подклассу, тогда будет вызван переопределенный метод.
Двойная отправка не существует в Java. Вы должны смоделировать это, например, используя Шаблон посетителя. В этом шаблоне, в основном, каждый подкласс реализует accept
метод и вызывает посетителя с this
в качестве аргумента и this
имеет в качестве типа времени компиляции этот подкласс, поэтому используется перегрузка нужного метода.
Причина в том, что 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
,