Передача параметра базового класса методу с сигнатурой подкласса

#java #inheritance

#java #наследование

Вопрос:

У меня простая иерархия, Base и Derived у меня есть их обычные значения.

 class Base{}
class Derived1 extends Base{}
class Derived2 extends Base{}
class Test {
    void method1() {
        Base b1 = new Derived1();
        Base b2 = new Derived2();

        call(b1);
        call(b2);
    }

    void call(Derived1 d){}
    void call(Derived2 d){}
}
  

Почему java не позволяет мне вызывать call метод с разными типами?

В нем говорится: The method call(Derived1) in the type Test is not applicable for the arguments (Base)

Разве он не может определить тип во время выполнения и вызвать соответствующий метод?

Ответ №1:

Разрешение метода в java является статическим. Другими словами, время компиляции.

Это имеет множество преимуществ. Например, этот код правильно определен как неуправляемый и не будет компилироваться:

 Base b3 = new Derived3();
call(b3); // does not compile in java.
// which is correct, as there is no call(Derived3) method at all.
  

Что еще более важно, каждый вызов метода в коде Java известен во время записи (время компиляции). Это означает, например, что вы можете щелкнуть CMD / CTRL по методу в вашем редакторе и перейти прямо к методу, который он вызывает. И это не предположение, что вы получаете с более динамичными языками, такими как python или javascript. Это неопровержимый факт, потому что это буквально то, что заканчивается в файле класса.

Java ИМЕЕТ динамическую отправку: «Идентификатор» метода (это комбинация имени, типов параметров, возвращаемого типа и класса, в котором он находится) разрешается во время записи (компиляции) и записывается в файл класса, но какая фактическая реализация этогов конечном итоге выбирается метод, эта часть является динамической:

 
class Base {
    void foo() {
        System.out.println("base");
    }
}

class Child extends Base {
    void foo() {
        System.out.println("child");
    }
}

Base b = new Child();
b.foo(); // prints "child"
  

«Идентификатор» foo() ищется в базовом классе, и поэтому этот код компилируется: Base имеет метод foo() . Но фактическая реализация, вызываемая во время выполнения, является динамической и, поскольку b ссылается на объект типа Child, в конечном итоге является его дочерней реализацией.

Комментарии:

1. ОК. Учитывая, что эта штука не работает, теперь мне нужно переделать то, что я пытался реорганизовать, используя шаблон проектирования: (

2. call Предположительно, переместить метод в базовый. Это или новый интерфейс, который Derived1 и Derived2 оба реализуют. Трудно сказать без более подробной информации.