Наследование Java по сравнению с наследованием С#
Предположим, что Java имеет эти иерархические классы:
class A
{
}
class B extends A
{
public void m()
{
System.out.println("B\n");
}
}
class C extends B
{
public void m()
{
System.out.println("C\n");
}
}
class D extends C
{
public static void main(String[] args)
{
A a = new D();
// a.m(); // doesn't work
B b = new D();
b.m();
C c = new D();
c.m();
D d = new D();
d.m();
}
}
Это (слепое) дублирование того же кода в С#:
using System;
class A
{
}
class B : A
{
public void M()
{
Console.WriteLine("B");
}
}
class C : B
{
public void M() // I need to use public new void M() to avoid the warning
{
Console.WriteLine("C");
}
}
class D : C
{
public static void Main(String[] args)
{
A a = new D();
// a.M(); // doesn't work
B b = new D();
b.M();
C c = new D();
c.M();
D d = new D();
d.M();
}
}
Когда я выполняю Java-код, я получил C-C-C
, тогда как С# возвращает B-C-C
.
Для меня результат С# имеет больше смысла, так как ссылка B вызывает свой собственный метод.
- Какова логика решения Java-дизайнера для печати
C-C-C
вместо B-C-C
? Я имею в виду, почему ссылка B использует переопределяющий метод в C? Какое преимущество этого подхода?
- Как я могу изменить код Java для печати
B-C-C
, как это делает С#? Я имею в виду, как я могу научить java вызывать метод точной ссылки, которую он использует?
- Как мне изменить код С# для печати
C-C-C
? Я имею в виду, как я могу научить С# вызывать метод переопределения?
Ответы
Ответ 1
Это для определения виртуальной функции:
виртуальная функция или виртуальный метод - это функция или метод, поведение которых может быть переопределено внутри наследующего класса функцией с той же сигнатурой. Это понятие является очень важной частью части полиморфизма объектно-ориентированного программирования (ООП).
В С# вы должны объявить метод как виртуальный, чтобы его можно было переопределить, как показано в MSDN:
Поскольку метод M
не является виртуальным, он выполнит b.M()
, даже если переменная b
на самом деле является экземпляром D
.
В Java каждый метод по умолчанию виртуальный, поэтому при переопределении метода (даже без аннотации @Override
) поведение b.M()
будет d.M()
, которое наследует поведение метода c.M()
.
Как я могу изменить код Java, чтобы распечатать B-C-C так же, как С#? Я имею в виду, как я могу научить java вызывать метод точной ссылки, которую он использует?
Вы просто не можете сделать это на Java. Метод M
в классе C
переопределяет метод M
в b
. Добавление модификатора final
в B#M
приведет к тому, что C
или другие b
дети не смогут переопределить метод M()
.
Как я могу изменить код С#, чтобы распечатать C-C-C? Я имею в виду, как я могу научить С# вызывать метод переопределения?
Измените метод M
в классе b
на virtual
и переопределите его в классе C
:
class B : A
{
public virtual void M()
{
Console.WriteLine("B");
}
}
class C : B
{
public override void M() // I need to use public new void M() to avoid the warning
{
Console.WriteLine("C");
}
}
Ответ 2
-
В java все методы виртуальны по умолчанию. И методы в производных классах переопределяют методы из базы. В С# это не так.
-
Похоже, вы не можете этого сделать. Но вы можете запретить производным классам переопределять этот метод, объявив его как final
.
-
Объявить этот метод с помощью ключевого слова virtual
в базовом классе и override
в производном.
Ответ 3
Какова логика решения Java-дизайнера для печати C-C-C вместо B-C-C?
Java делает методы виртуальными по умолчанию. Раньше было понимание того, что это была хорошая практика, и во времена рождения Java, возможно, был на вершине такого понимания.
В наши дни - в то время как многие из них придерживаются этого - есть и большая устойчивость к нему (особенно, поскольку виртуальные методы открывают класс для неожиданных вариантов выбора подкласса).
(NB, здесь нет правильного или неправильного значения, каждый подход имеет свои преимущества и недостатки.)
Как я могу изменить код Java, чтобы распечатать B-C-C так же, как С#?
Объявите их final
.
Как я могу изменить код С# для распечатки C-C-C?
Объявить их virtual
или abstract
в классе, который их определяет (B
в вопросе), и override
в дочерних классах.
Ответ 4
Какова логика решения Java-дизайнера для печати C-C-C вместо B-C-C? Я имею в виду, почему ссылка B использует переопределяющий метод в C? Какое преимущество этого подхода?
Посмотрите, что вы вызываете метод из объекта класса D, который унаследовал метод M из класса C. Тип ссылки не имеет значения в Java - важная часть - это класс, на который ссылается объект.