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