Перегрузка метода 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 Перегрузка:

  1. Когда метод вызывается (§15.12), число фактических аргументов (и любых явных аргументов типа) и типы аргументов во время компиляции используются во время компиляции, чтобы определить сигнатуру метода, который будет вызван (§15.12.2).
  2. Если метод, который должен быть вызван, является методом экземпляра, фактический метод, который должен быть вызван, будет определен во время выполнения, используя динамический поиск метода (§15.12.4).

Итак, в вашем случае:

  1. Аргумент метода (this) имеет тип времени компиляции Parentи так метод print(Parent) вызывается.
  2. Если 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,

Другие вопросы по тегам