Наследование 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 - важная часть - это класс, на который ссылается объект.