Почему методы виртуальные по умолчанию в Java, но не виртуальные по умолчанию в С#?

В Java методы по умолчанию виртуальны; С# - противоположное.

Что лучше? Каковы преимущества и недостатки в каждом подходе?

Ответ 1

Андерс Хейлсберг: (ведущий архитектор С#)

Есть несколько причин. Один представление. Мы можем заметить, что люди пишут код на Java, они забывают чтобы отметить их методы окончательные. Поэтому эти методы являются виртуальными. Поскольку они виртуальные, они не также выполняйте. Там просто эксплуатационные издержки, связанные с являясь виртуальным методом. Вон тот проблема.

Более важной проблемой является управление версиями. Есть две школы мысли о виртуальные методы. Академическая школа мысли говорит: "Все должно быть виртуальный, потому что я, возможно, захочу когда-нибудь переопределите его". Прагматичный школа мысли, которая исходит из создание реальных приложений, работающих в реальный мир, говорит: "Мы должны быть действительно заботясь о том, что мы делаем виртуальная".

Когда мы делаем что-то виртуальное в платформе, мы делаем очень много promises о том, как он развивается в будущее. Для не виртуального метода мы обещайте, что когда вы позвоните метод, x и y. Когда мы публиковать виртуальный метод в API, мы не только обещают, что, когда вы позвоните этот метод, x и y произойдет. Мы также обещаю, что когда вы переопределите этот метод, мы будем называть это в этом особая последовательность в отношении эти другие и государство будут в этом и том инварианте.

Каждый раз, когда вы говорите виртуальный в API, вы создаете обратный вызов. В виде разработчик инфраструктуры OS или API, вы должны быть очень осторожны что. Вы не хотите, чтобы пользователи переопределяли и зацепление в любой произвольной точке в API, потому что вы не можете сделайте эти promises. И люди могут не полностью понять promises, они делают, когда они что-то делают виртуальные.

Ответ 2

Java-путь более простой, С# путь более гранулированный, более безопасный и более эффективный по умолчанию. Что лучше в глазах пивного держателя.

Ответ 3

.Net заставляет программиста определять, какие функции могут быть переопределены, тогда как функции Java по умолчанию могут быть переопределены, если не используется последнее ключевое слово.

Если вы сильный сторонник Принцип Open/Close, вы можете поддерживать способ Java. Лучше всего разрешить расширять классы и переопределять методы таким образом, чтобы базовая функциональность/код не был затронут. По этой причине я поддерживаю/предпочитаю Java-путь. Если бы я рассматривал вопрос с другой точки зрения, мое мнение может быть обратным.

Ответ 5

Есть две основные причины, по которым виртуальный по умолчанию намного лучше, чем не виртуальный.

  • Основными принципами полезности ООП являются принцип подписи Лискова, полиморфизм и поздняя привязка. Я использую шаблон стратегии все время и для этого хочу, чтобы мои методы были виртуальными. Если вы поклонник принципа Open/closed, вам больше нравится философия Java. Вы можете изменить поведение, не изменяя исходный код. Вы можете сделать это с помощью инъекции зависимостей и виртуальных методов.
  • Если вы вызываете не виртуальный метод, то вы хотите узнать из своего кода, какой метод класса вы вызываете. Недостатком .net является то, что вы не можете знать это из своего кода.
  • Еще одно преимущество виртуального метода заключается в том, что гораздо проще протестировать ваш код, потому что вы можете делать Mocks ваших (или сторонних) классов. Тестирование с помощью Mockito на Java очень просто.

Пример

В Java, если вы определяете ClassB как

public class ClassB extends ClassA {
    @Override 
    public void run() {
    }
}

и объект

ClassA obj=new ClassB();

Если вы вызываете obj.run(), как вы узнаете, соответствует ли этот код правилам полиморфного принципа open/close или он будет кодировать метод, относящийся к ClassA? В Java вы будете знать, что всегда существует полиморфизм. Легче сделать издевки, и легче расширить классы и следовать принципу подписи Лискова.

С другой стороны, статические методы ограничены классом, поэтому, если вы хотите вызвать метод, связанный с ClassA, вы можете определить этот метод следующим образом:

public static run(ClassA obj)

и вы можете вызвать его с помощью

ClassB obj=new ClassB();
ClassA.run(obj);

и из кода вы узнаете, что метод, который вы вызываете, определяется в ClassA, а не в ClassB. Также в этом случае у вас не будет накладных расходов на виртуальные методы. (Обратите внимание, что JIT также уменьшит накладные расходы виртуальных методов во многих случаях).

Для С#, если причина, по которой метод не виртуальный, должен иметь возможность определять его в подклассе, но не включать полиморфизм, вы, вероятно, подклассифицируете по какой-либо причине.

Если это для дизайна, я бы предложил сделать класс запечатанным (final in java) вместо индивидуальных методов, если это возможно.

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

Ответ 6

Это возмущение во времени. С++ использует ключевое слово virtual и final по умолчанию.

Java следует за С++ и пытается сделать все возможное и улучшить свои недостатки. Опасности чрезмерного использования наследования не выявлены, поэтому Java предпочитает использовать последнее ключевое слово и виртуальное по умолчанию.

С# следует за Java и имеет преимущество задним числом. Андерс решил вернуться к конвенции С++ после наблюдения за опытом Java.

Ответ 7

Как всегда, есть плюсы и минусы. С# сделал AOP сложнее реализовать, но задним числом может быть 20/20, как упоминалось другими. Все это сводится к тому, считаете ли вы, что классы должны быть разработаны для расширения или просто оставлены открытыми для непредвиденных модификаций поведения. При разработке языка это сложный вопрос для ответа. Я думаю, что отраслевой опыт склоняется к более консервативному подходу, который занимает С#.