2015-02-11 4 views
0

Я изо всех сил пытался понять часть кода Java, которая использует наследование для вычисления результата. Код состоит в следующем:Четыре уровня наследования в Java

class A 
{ 
    public int calc(double num) { 
     System.out.println("calc de A"); 
     return (int)(num+1); 

    } 
} 

class B extends A 
{ 
    public int calc(long num) { 
     System.out.println("calc de B"); 
     return (int)(num+2); 

    } 
} 

class C extends B 
{ 
    public int calc(int num) { 
     System.out.println("calc de C"); 
     return num+3; 

    } 
} 

class D extends C 
{ 
    public int calc(float num) { 
     System.out.println("calc de D"); 
     return (int)(num+4); 

    } 
} 

class Main 
{ 
    public static void main(String[] args) 
    { 
     int num1 = 10; 
     long num2 = num1; 

     A a1 = new D(); 
     A a2 = new D(); 

     int result = a1.calc(num1) + a2.calc(num2); 

     System.out.println("Numero: "+ result); 
    } 
} 

Я и мои друзья думают, что выход консоли должен быть:

calc de C 
calc de B 
Numero: 25 

Но запустить его в Eclipse, и в Ideone (ссылка: http://ideone.com/CTdklv), чтобы проверить его и полученный ответ является

calc de A 
calc de A 
Numero: 22 

Может кто-то объяснить, почему выход 22, если сигнатуры методов различны.

Мы верим, что если вы вызовете метод calc и используя в качестве параметра, вы должны перейти к методу calc класса B вместо класса A.

Прилагаю вашу помощь! Спасибо.

+2

Ваши объекты объявлены как 'A'. Ваш метод 'calc' имеет несовместимую подпись и, следовательно, не перекрывает друг друга. Следовательно, в этой точке можно вызвать только «A.calc». – njzk2

+0

Точно как @ njzk2 сказал. Вы не получаете полиморфизм, если вы не переопределяете метод должным образом. И без полиморфизма вы не можете получить динамическую отправку из базового класса. – jepio

+0

Я знаю, что они не переопределяют друг друга, но вы называете их различными типами числовых значений. Должен ли calc (int) вызывать метод, соответствующий методу B calc, и метод calc (float) вызывать метод D calc? – mfsierpe

ответ

1

В Java, который перегрузок можно назвать определяется типом опорной переменной (15.12.1):

Если форма (выражения вызова метода) является ExpressionName. [TypeArguments] Идентификатор, затем класс или интерфейс для поиска - это объявленный тип T переменной, обозначенной ExpressionName, если T - тип класса или интерфейса, или верхняя граница T, если T является переменной типа.

С a1 и a2 объявлены A, вы можете назначить D для них, но вы можете только вызвать метод из A.

Вы можете видеть, что это работает таким образом, если вы измените типы вокруг немного:

class A { 
    void m(int i) {} 
} 

class B extends A { 
    void m(double d) {} 
} 

A a = new B(); 

double d = 0d; 
a.m(d); // won't compile 
     // will say something like 
     // 'double can not be converted to int' 

Так что если вы измените a1 и a2 к D

D a1 = new D(); 
D a2 = new D(); 

выход должен быть вы ожидали, потому что перегрузки в B, C и D доступны для звонка.


Обратите внимание, что он также работает для переопределений.

class A { 
    protected void m() {} 
} 

class B extends A { 
    @Override 
    public void m() {} 
} 

// in another package... 
B b = new B(); 
b.m(); // call m because it is public in B 
A a = b; 
a.m(); // won't compile because it is protected in A 

Какие методы доступны по типу ссылки.

+1

Если вы назначили A a = new B(); в этом случае и метод calc был должным образом отменен, a.calc() должен вызвать метод calc B справа? – mfsierpe

+0

Примите во внимание, что класс D может * переопределить * метод calc класса A и если новый D хранится в A x, x.calc *, безусловно, вызовет метод D's calc. * – laune

+0

@mfsierpe Исправить. В вашем примере приведение просто исключает поиск * перегруженных * методов. Попробуйте запустить ваш пример с помощью D a1 = ... и D a2 = ..., а затем перегрузите разрешение, и вы получите ожидаемый результат. – laune

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