Почему все методы Java неявно переопределены? - PullRequest
21 голосов
/ 07 мая 2009

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

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

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

Ответы [ 6 ]

27 голосов
/ 08 мая 2009

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

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

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

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

РЕДАКТИРОВАТЬ: Таким образом, меня вызывают на мои утверждения. Следующий абзац - это предположение с моей стороны, а не констатация факта.

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

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

Встраивание

* 1022 виртуальные таблицы *

19 голосов
/ 07 мая 2009

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

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

Возможно, немного круглым.

11 голосов
/ 08 мая 2009

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

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

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

3 голосов
/ 08 мая 2009

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

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

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

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

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

1 голос
/ 08 мая 2009

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

1 голос
/ 08 мая 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...