2015-04-14 2 views
0

Для следующего кода после ввода параметров «A2 a» и «B2 b» в классе A2 и классе B2 результат изменился. Интересно, почему?Java - выход изменился при добавлении параметров (аргументов)

А1 Класс: Класс

public class A2 
{ 
    public void m1(A2 a) 
    { 
     System.out.println("A2"); 
    } 

} 

В2:

public class B2 extends A2 
{ 
    public void m1(B2 b) 
    { 
     System.out.println("B2"); 
    } 

} 

Основной класс (запустить программу):

public class D2 
{ 
    public static void main(String[] args) 
    { 
     A2 x = new A2(); 
     A2 y = new B2(); 
     B2 z = new B2(); 

     y.m1(); 
    } 

} 
+1

Я предполагаю, что это не последняя версия 'D2', или она не будет компилироваться. Какой аргумент вы перешли на 'm1'? – jas

ответ

1

Перед тем, как вставить параметры, B2.m1() отменяют A2.m1(), поэтому метод называется зависит только от типа исполнения приемника.

После того, как вы вставили параметры, два метода m1 имеют разные типы подписей, а B2.m1(B2 b) больше не переопределяет A2.m1(A2 a). При этом вызванный метод зависит от типов времени компиляции как приемника, так и отправляемого параметра.

Дано:

A2 x = new A2(); 
    A2 y = new B2(); 
    B2 z = new B2(); 

    y.m1(x); // calls A2.m1(A2 a) 
    y.m1(z); // calls A2.m1(A2 a) 
    z.m1(x); // calls A2.m1(A2 a) 
    z.m1(z); // calls B2.m1(B2 b) 

Перед тем, как добавлен параметр:

y.m1(); // would have called B2.m1() 

является то, что несоответствие вы видите?

EDIT: Детали слишком подробно добавлены ниже.

Прежде всего вы можете думать о подписи как в основном имя метода в сочетании с номером и типами формальных параметров. Вы можете увидеть их в виде «m1: (LA2;) V» или «m1: (LB2;) V» (метод с именем «m1», который принимает 1 параметр типа класса A2 и возвращает Void). Но здесь я просто напишу m1(A2) или m1(B2), чтобы это стало проще.

В основном два шага для определения того, какой метод окончательно вызван. Шаг времени компиляции и шаг выполнения. Шаг времени компиляции определяет подпись метода, который должен быть вызван. Шаг выполнения выбирает фактический метод для вызова путем поиска того, что с этой сигнатурой является классом объекта по мере его создания. Если он не найдет его там, он перемещается вверх по иерархии классов до тех пор, пока не будет найден метод с этой сигнатурой (там должно быть где-то или оно не было бы скомпилировано).

Чтобы найти подпись, используются типы времени компиляции (также называемые «статическими типами») (для объявления переменной используются те, которые были указаны ).Это касается как приемника, так и параметров.

Итак, давайте посмотрим на подписи, выбранной в каждом случае:

y.m1(x) 

у объявляются быть A2 поэтому единственно возможная подпись m1(A2).

y.m1(z) 

Опять y является A2 поэтому единственная возможность m1(A2). Тот факт, что z является B2, прекрасен с , вы всегда можете пройти B2, где ожидается A2. (На данный момент в этом процессе, тот факт, что время выполнения y будет создан как B2 не имеет никакого значения!)

z.m1(x) 

z является B2. В B2 есть две возможности. m1(B2) и унаследованный m1(A2). Поскольку x - A2, первое невозможно (вы не можете пройти A2, где ожидается B2), поэтому один раз снова мы остаемся с m1(A2).

z.m1(z) 

z является B2. В B2 есть две возможности. m1(B2) и унаследованный m1(A2). С z - B2, возможны оба варианта. Здесь у Java есть правило для выбора, которое должно взять одно с самым конкретным (наиболее производным) аргументом типа , поэтому выбирается m1(B2).

Во всех вышеперечисленных случаях больше нечего решать во время выполнения, так как в каждом случае есть только один метод с данной сигнатурой. Возьмите случай y.m1(z). Во время выполнения Java будет искать метод с сигнатурой m1(A2), как определено на этапе компиляции.

Сначала он будет выглядеть в классе B2 с y был создан как объект B2. Но здесь нет метода с этой подписью (есть только m1(B2)). Не имеет значения, что z является объектом B2, как только подпись определяется на этапе компиляции, только метод с этой сигнатурой будет называться во время выполнения . Итак, Java поднимается по иерархии и находит метод с правильной подписью в классе A2, и, следовательно, результаты - это то, что вы видите. (Обратите внимание, что это концептуально, а фактический код, созданный для его правильного метода, является более умным.)

Теперь, прежде чем вы добавили параметр, оба метода m1 были названы «m1» и не имели параметры, следовательно, у них была та же подпись m1().Так, глядя на

y.m1() 

Compile времени шаг: y является A2 поэтому единственная возможность выбирать является m1().

Затем во время выполнения, так как y был создан как B2, Java начинается в классе B2 ищет метод с этой подписью. Он находит один! Метод m1() в B2 имеет ту же подпись, что и метод m1() в A2. Другими словами, в отличие от вышеприведенных случаев, B2 .m1 действительно отменяет A2.m1. Так что java счастлив назвать версию m1() в B2, как вы видели перед добавлением параметров.

Так что это история. Я знаю, что это сбивает с толку, но это действительно ключ к пониманию Java (и других объектов ориентированных языков) и замечательно, что вы копаете в этом.

Прочитайте все, что вы можете о переопределении и перегрузке, полиморфизме, динамической диспетчеризации и виртуальных таблицах методов .

Я думаю, что наиболее важные моменты:

- Только если вы переопределить метод вы получаете динамическую диспетчеризацию на основе типа во время выполнения приемника

- метод в производный класс только переопределяет метод в базовом классе, если подпись совпадает с

+0

Да, вы на месте, но я до сих пор не понимаю, как простой параметр может изменить выход. Я не вижу никакой корреляции между параметрами и выходом. Не могли бы вы сделать пошаговую схему кода, чтобы объяснить проблему? Каковы 2 типа подписей после ввода параметров? – dcdcd

+0

См. Обновление, надеюсь, что я смог это объяснить! – jas

Смежные вопросы