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
, вы можете получить доступ только к одному методу извне, а именно к одному из производного класса. Доступ к методу базового класса можно получить с помощью методов производного класса, но не внешних пользователей производного класса.