Подтвердить что ты не робот

Перегрузка 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);
    }
}
4b9b3361

Ответ 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.