Переопределенный метод Java не вызывается, даже если он существует
Мне нужно обеспечить прямую совместимость моего приложения с зависимостью, которая вводит новые методы подключения к суперклассу, который расширяет мое приложение. Прямой подход к введению вновь добавленных методов (игнорируемый старой версией, которую я создаю и использую новой) перестал работать, как только я начал определять типы возвращаемых данных, которые являются подтипами объявленных.
Когда я вызываю мой переопределенный метод непосредственно как foo.bar("")
метод суперкласса вызывается. Тем не менее, когда я вызываю его через отражение от отладчика foo.getClass().getMethod("bar", String.class).invoke(foo, "")
, он вызывает переопределенный метод, как и ожидалось. Метод вызывается правильно, когда его возвращаемый тип сужается до того же типа, что и переопределенные методы возврата, ранее это был подтип.
1 ответ
В случае переопределений с ковариантными типами возврата, компилятор java генерирует методы моста, которые имеют те же эффекты, что и их объявленные аналоги, но имеют тип возврата переопределенного метода. Это необходимо, поскольку JVM идентифицирует методы по имени, списку аргументов и, в отличие от языка программирования Java, по типу возвращаемого значения. Компилятор сделает это, если и только если он знает, что метод переопределяет, и возвращаемый тип является подтипом типа, возвращаемого методом суперкласса. (Обратите внимание, что решение не зависит от @Override
аннотация).
В этом случае компилятор не знает, что вновь добавленный метод должен быть переопределен (поскольку старая версия зависимости вообще не объявляет его), поэтому нет способа узнать тип возвращаемого коварианта. В результате не существует сгенерированного метода моста, который бы JVM идентифицировал как переопределение, поэтому он завершает поиск реализации метода дальше по дереву наследования.
Есть несколько способов обойти это.
- Убедитесь, что переопределяющие методы, обеспечивающие прямую совместимость, имеют тот же тип возврата, что и их родители. Таким образом, методы моста не нужны.
- Создавайте новые версии зависимостей и работайте над обеспечением обратной совместимости. Наиболее заметным недостатком здесь является то, что минимальная поддерживаемая версия не является заявленной, скажем, maven POM.
- Используйте манипулирование байт-кодом для явного создания методов моста. Я не даю ссылку здесь, чтобы отговорить читателей сделать это.