В чем разница между статическим и динамическим полиморфизмом в коде
Что проверяют
Понимаете ли вы, в какой момент выбирается вызываемый метод: компилятором или JVM во время выполнения. Следом обычно дают задачку, где перегрузка и переопределение смешаны в одном примере, и просят сказать, что выведется.
Как отвечать
- статический (compile-time): перегрузка, overloading. Методы с одним именем и разными параметрами, а выбор делает компилятор по объявленным типам аргументов. Сюда же иногда относят обобщения (
List<T>): параметрический полиморфизм, который проверяется при компиляции, а в рантайме стирается; - динамический (runtime): переопределение, overriding. Подкласс заменяет реализацию метода родителя или интерфейса, а какая версия вызовется, решает JVM по фактическому типу объекта. В байткоде это
invokevirtualилиinvokeinterface.
Ключевой пример: есть print(Object o) и print(String s), а вызов идёт с Object x = "hi". Выберется print(Object), потому что перегрузка смотрит на объявленный тип переменной. А x.toString() вызовет реализацию String, потому что переопределение смотрит на реальный объект.
Что не участвует в динамической диспетчеризации: static, private и final методы, а также поля. Статический метод в наследнике не переопределяет, а скрывает родительский (method hiding), и Parent p = new Child(); p.staticMethod() вызовет версию Parent.
Про цену стоит сказать трезво: виртуальный вызов в HotSpot почти ничего не стоит. JIT видит, что в точке вызова встречается один или два типа, девиртуализирует вызов и инлайнит метод. Отказываться от интерфейсов в пользу перегрузки ради скорости не нужно.
Где спотыкаются
Называют перегрузку операторов частью статического полиморфизма Java. Пользовательской перегрузки операторов в Java нет, + для строк встроен в сам язык.
Считают, что перегрузка выбирается по реальному типу объекта, и ошибаются в задаче с Object x = "hi". Этот пример задают очень часто.
Пытаются переопределить статический метод и удивляются, что вызов идёт по типу ссылки. Аннотация @Override здесь сразу дала бы ошибку компиляции, поэтому её стоит ставить всегда.