Подтвердить что ты не робот

Почему все java-методы неявно переопределяются?

В С++ я должен явно указать "виртуальное" ключевое слово, чтобы сделать функцию члена "overridable", поскольку в ней возникают накладные расходы на создание виртуальных таблиц и vpointers, когда функция-член становится переопределяемой (поэтому каждая функция-член неявно не зависящие по производительности).

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

Тот же метод используется и на С#. Мне интересно, почему Java отмахивалась от этого поведения и делала каждый метод переопределяемым по умолчанию и предоставляла возможность отключать переопределение поведения при явном использовании ключевого слова final.

4b9b3361

Ответ 1

Лучший вопрос: "Почему у С# есть не виртуальные методы?" Или, по крайней мере, почему они не являются виртуальными по умолчанию с возможностью отмечать их как не виртуальные?

В С++ есть идея (как красиво указал Брайан), что, если вы этого не хотите, вы не платите за нее. Проблема в том, что если вы этого хотите, это обычно означает, что вы в конечном итоге платите через нос за это. В большинстве реализаций Java они специально разработаны для множества виртуальных вызовов; реализация vtable имеет тенденцию быть быстрой, едва более дорогой, чем не виртуальные вызовы, что означает, что основное преимущество не виртуальных функций теряется. Кроме того, компиляторы JIT могут встроить виртуальные функции во время выполнения. Таким образом, по соображениям эффективности очень мало причин использовать не виртуальные функции.

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

Полиморфизм также диктует, что сам объект должен контролировать то, что он делает. Это поведение не должно определяться, считает ли клиент FooParent или FooChild.

ИЗМЕНИТЬ: Итак, меня приглашают на мои утверждения. Следующий параграф - это гипотеза с моей стороны, а не утверждение факта.

Интересным побочным эффектом всего этого является то, что Java-программисты очень сильно используют интерфейсы. Поскольку оптимизация виртуальных методов делает стоимость интерфейсов по существу несуществующей, они позволяют использовать список (например) вместо ArrayList и переключать его на LinkedList на более позднюю дату с простым однострочным изменением и никакого дополнительного штрафа.

РЕДАКТИРОВАТЬ: Я также расскажу о нескольких источниках. Хотя они и не являются исходными источниками, они приходят из Sun, объясняя некоторые работы на HotSpot.

Inlining

VTable

Ответ 2

Взято из здесь (# 34)

В Java нет виртуального ключевого слова потому что все нестатические методы всегда используйте динамическое связывание. В Java, программисту не нужно решать использовать динамическое связывание. Причина виртуальная существует в С++, так что вы может оставить его для небольшого увеличения в эффективности, когда вы настраиваете (или, иначе говоря, "Если вы не используете его, вы не платите за это" ), что часто приводит к путанице и неприятные сюрпризы. Финал ключевое слово предоставляет некоторую широту для эффективности - это говорит компилятора, что этот метод не может быть переопределяется и, следовательно, может быть статически связанный (и сделанный inline, таким образом, используя эквивалент С++ не виртуальный вызов). Эти оптимизации до компилятора.

Немного круговой, возможно.

Ответ 3

Итак, обоснование Java, вероятно, что-то вроде этого: вся точка объектно-ориентированного языка состоит в том, что вещи могут быть расширены. Таким образом, с точки зрения чистого дизайна, на самом деле мало смысла рассматривать расширяемый как "частный случай".

Помните, что Java обладает роскошью компиляции во время выполнения. Поэтому некоторые из аргументов производительности в компиляции С++ выходят из окна. В С++, если класс может быть переопределен, компилятор должен выполнить дополнительные шаги. В Java нет никакой тайны: в любой момент времени JVM знает, переопределен ли какой-либо конкретный метод/класс или нет, и что, по сути, имеет значение.

Обратите внимание, что ключевое слово final по существу относится к разработке программы, а не к оптимизации. JVM не нуждается в этой информации, чтобы узнать, отменен ли класс/метод!!

Ответ 4

Если вопрос задается вопросом, что лучше подходит между java и С++/С#, то он уже обсуждался в противоположном направлении в другом потоке, и многие ресурсы доступны в сети

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

http://www.artima.com/intv/nonvirtual.html

Недавнее введение аннотации @Override и ее широкое применение в новом коде предполагает, что точный ответ на вопрос "Почему все java-методы неявно переопределяются?" действительно, потому, что дизайнер ошибся. (И они уже исправили его)

О! Я собираюсь получить отрицательный голос за это.

Ответ 5

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

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

Три разных приоритета дизайна, которые приводят к различным вариантам.

Ответ 6

Я бы сказал, что в Java стоимость виртуального метода низкая по сравнению с целыми расходами VM. В С++ это значительная стоимость по сравнению с сборочным фоном C. Никто не решил бы, чтобы все методы вызывались через указатель по умолчанию в результате миграции C на С++. Это слишком большие изменения.