javaforward-compatibilityjava-bridge-method

Overridden Java method not called even though it exists


I need to ensure forward compatibility of my application with a dependency that introduced new hook methods to the superclass my application extends. The straightforward approach of introducing the newly added methods (to be ignored by the old version I build against and utilized by the new one) stopped working as soon as I started to define return types that are subtypes of declared ones.

When I call my overridden method directly as foo.bar("") a superclass method gets called. However, when I invoke it through reflection from debugger foo.getClass().getMethod("bar", String.class).invoke(foo, ""), it calls the overridden method as expected. The method gets called correctly when its return type is narrowed to the same type overridden methods return, it was a subtype before.


Solution

  • In case of overrides with covariant return types, java compiler generates bridge methods that has the same effects as their declared counterparts but have the return type of overridden method. This is needed as JVM identifies methods by its name, argument list and, unlike Java programing language, its return type. Compiler will do this if and only if it is aware that the method overrides and the returned type is a subtype of the type returned by superclass' method. (Note that the decision does not depend on @Override annotation).

    In this case, compiler is not aware the newly added method is supposed to be an override (because old version of dependency does not declare it at all) so there is no way to know the return type of covariant. As a result, there is no bridge method generated that would JVM identify as override so it ends up searching for method implementation further up the inheritance tree.

    There are several ways to workaround this.