Java Vs С#: подклассы Java и С# с переопределением метода выводят разные результаты в одном сценарии

Ok! У меня есть тот же код, написанный в Java и С#, но результат отличается!

class A
{
    public void print() 
    {
        Console.WriteLine("Class A");
    }
}

class B : A 
{
    public void print()
    {
        Console.WriteLine("Class B");
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new B();
        a.print();
        Console.Read();
    }
}

Выход: Класс A. Он находится на С#.

Но когда тот же код запускался в Java, результат был класса B. Вот код Java:

class A
{
    public void print() 
    {
        System.out.println("Class A");
    }
}

class B extends A 
{
    public void print()
    {
        System.out.println("Class B");
    }
}


public class Program{

 public static void main(String []args){
    A a = new B();
    a.print();
 }
}

Итак, почему это показывает разные результаты? Я знаю, что в Java все методы по умолчанию являются виртуальными, поэтому Java выводит класс B.

Другое дело, что оба языка утверждают, что они появились или вдохновлены С++, то почему они показывают разные результаты, в то время как оба имеют одинаковый базовый язык (Say).

И что делает эта строка A a = new B(); на самом деле? Является ли a объектом хранения класса B? Если это так, то почему С# отображает Класс A, а Java показывает Класс B?

ПРИМЕЧАНИЕ Этот вопрос задавался в интервью с тем же кодом, который был приведен выше. И я ответил с выходом класса B (по отношению к Java), но он сказал, что Класс A будет правильным выходом.

Спасибо!

Ответ 1

В Java нестатические методы являются виртуальными, тогда как в С# они не являются. Вам нужно будет использовать ключевые слова virtual и override для вашего метода печати, чтобы получить такое же поведение в С#.

Полиморфное поведение в С#:

class A
{
    public virtual void print() 
    {
        Console.WriteLine("Class A");
    }
}

class B : A 
{
    public override void print()
    {
        Console.WriteLine("Class B");
    }
}

Edit

Возвращаясь к исходному коду С#, вы получите предупреждение о времени компиляции на B.print, когда вы используете ту же подпись метода как в подклассе, так и в его суперклассе, а именно:

Ключевое слово 'new' требуется для 'print', потому что оно скрывает метод 'MyNamespace.A.print()'

Это хороший признак того, что этот метод не будет называться полиморфно/практически. Чтобы избежать предупреждения (и сохранить исходное поведение С#), в B вам нужно будет добавить new:

    public new void print()

Ответ 2

Это связано с тем, что в методах С# производных классов скрываются, а не переопределяются методы их базового класса. Методы, которые вы хотите переопределить, должны быть явно помечены ключевым словом virtual в базе и с ключевым словом override в производных классах.

Напротив, в Java все методы по умолчанию виртуальны: для переопределения достаточно просто указать одну и ту же подпись.

Вот как сделать вашу программу на С# эквивалентной программе Java:

class A
{
    public virtual void print() // Add "virtual"
    {
        Console.WriteLine("Class A");
    }
}

class B : A 
{
    public override void print()// Add "override"
    {
        Console.WriteLine("Class B");
    }
}

После A a = new B() переменная a удерживает объект B, но вывод "Class A"! Не следует ли вызвать метод класса B?

Когда вы скрываете метод, а не переопределяете его, ваш производный класс сохраняет оба метода - тот, что находится в базовом классе, и тот, который находится в производном классе. Оба этих метода остаются доступными для внешних абонентов. Они могут решить, какой из двух методов нужно вызвать, используя объект соответствующего статического типа. Вот пример:

    B b = new B();
    b.print();      // Prints "Class B"
    ((A)b).print(); // Prints "Class A"

Демо на идеоне.

Когда вы используете virtual/override, вы можете получить доступ только к одному методу извне, а именно к одному из производного класса. Доступ к методу базового класса можно получить с помощью методов производного класса, но не внешних пользователей производного класса.