Практическая ситуация: класс-потомок объявил статический метод с тем же именем, что у родителя. Какую реализацию выберет Java при обращении через переменную типа родителя?
Java выберет статический метод, соответствующий объявленному типу переменной, а не фактическому типу объекта. Статические методы не участвуют в динамическом полиморфизме: метод потомка скрывает метод родителя, но не переопределяет его.
Статические методы относятся к классу, а не к отдельному экземпляру. Такой подход позволяет вызывать поведение без создания объекта и однозначно связывать его с конкретным классом.
Для экземплярных методов Java поддерживает позднее связывание: реализация выбирается по фактическому типу объекта. Для статических методов используется раннее связывание на этапе компиляции, поскольку объектное состояние и динамический тип экземпляра не определяют вызов.
Если потомок объявляет статический метод с той же сигнатурой, возникает не переопределение, а скрытие метода. Поэтому изменение фактического типа объекта не изменит выбранную реализацию, если тип выражения остаётся прежним.
Это может привести к неожиданному результату при чтении кода: визуально вызов выглядит полиморфным, но компилятор ориентируется на тип переменной. Ошибка особенно вероятна, если статические методы вызываются через ссылку на объект вместо имени класса.
Рассмотрим минимальный пример:
Будет выведено parent, потому что выражение value.show() имеет объявленный тип Parent. Фактический объект Child для выбора статического метода не учитывается.
Корректнее и понятнее обращаться к статическим методам через имя класса: Parent.show() или Child.show(). Вызов через переменную допустим, но вводит в заблуждение и обычно сопровождается предупреждением компилятора.
Статический метод потомка не может быть помечен аннотацией @Override: он не переопределяет метод родителя. Если у методов совпадают имя и параметры, говорят о скрытии; выбор зависит от типа выражения, через которое выполняется вызов.
Ограничение сохраняется даже при наличии объекта потомка. Если требуется полиморфное поведение, метод должен быть экземплярным и переопределяемым, а не статическим. Статические методы подходят для операций, не зависящих от состояния конкретного объекта, но плохо подходят для поведения, которое должно изменяться в подклассах.
В библиотеке базовый класс предоставляет статический метод parse, а специализированный класс объявляет метод с тем же именем. Разработчик хранит экземпляр специализированного класса в переменной базового типа и ожидает, что будет вызвана специализированная реализация. В результате вызывается метод базового класса, что может привести к неверной валидации или выбору неподходящего формата.
Возможны два решения. Можно оставить статические методы и всегда вызывать их через конкретное имя класса: это просто и явно, но не поддерживает полиморфизм. Можно сделать метод экземплярным и переопределяемым: это обеспечивает динамический выбор реализации, но требует объекта и допускает влияние состояния экземпляра.
Если поведение действительно зависит от конкретного типа объекта, выбирают экземплярный метод. Вызов становится полиморфным, а код перестаёт зависеть от объявленного типа ссылки; результатом является корректный выбор специализированной реализации.
Да, такой синтаксис допускается Java, но он не превращает метод в экземплярный. Выбор всё равно выполняется по объявленному типу выражения на этапе компиляции, а сам объект не используется для динамического диспетчеризации.
Практически это считается плохим стилем: запись создаёт впечатление полиморфного вызова. Предпочтительна форма через имя класса, которая явно показывает принадлежность метода к классу.
Это будет не скрытие одного метода другим, а перегрузка с разными сигнатурами. Для выбора конкретного варианта компилятор учитывает типы аргументов и доступные методы в контексте объявленного типа выражения.
Динамический тип объекта всё равно не включается в выбор, поскольку оба метода остаются статическими. Если требуется переопределение поведения, совпадения имени недостаточно: метод должен быть экземплярным, а его сигнатура — соответствовать методу родителя.
Переопределение предполагает вызов через объект и позднее связывание с реализацией фактического типа. Статический метод принадлежит классу, поэтому у него нет полиморфного экземплярного вызова, который можно было бы переопределить.
При совпадении сигнатуры Java разрешает подклассу объявить собственный статический метод. Это создаёт два метода, выбор между которыми определяется типом выражения; такой механизм называется скрытием, а не переопределением.