0

我需要确保我的应用程序与向我的应用程序扩展的超类引入新钩子方法的依赖项的前向兼容性。当我开始定义作为声明的子类型的返回类型时,引入新添加的方法(被我构建的旧版本忽略并被新版本使用)的直接方法停止工作。

当我直接调用我的重写方法作为foo.bar("")超类方法被调用时。但是,当我通过 debugger 的反射调用它时foo.getClass().getMethod("bar", String.class).invoke(foo, ""),它会按预期调用被覆盖的方法。当该方法的返回类型缩小到与被覆盖方法返回的相同类型时,该方法被正确调用,它之前是一个子类型。

4

1 回答 1

2

在具有协变返回类型的覆盖的情况下,java 编译器生成桥接方法,其效果与其声明的对应物具有相同的效果,但具有被覆盖方法的返回类型。这是必需的,因为 JVM 通过名称、参数列表以及与 Java 编程语言不同的返回类型来识别方法。当且仅当编译器知道方法覆盖并且返回的类型是超类方法返回的类型的子类型时,编译器才会这样做。(请注意,该决定不依赖于@Override注释)。

在这种情况下,编译器不知道新添加的方法应该是一个覆盖(因为旧版本的依赖根本没有声明它)所以没有办法知道协变的返回类型。结果,没有生成任何桥接方法,JVM 会将其识别为覆盖,因此它最终会在继承树的上方搜索方法实现。

有几种方法可以解决此问题。

  • 确保以这种方式确保前向兼容性的覆盖方法具有与其父级相同的返回类型。所以不需要桥接方法。
  • 针对新版本的依赖项进行构建,并努力确保向后兼容性。这里最显着的缺点是支持的最低版本不是由 maven POM 声明的版本。
  • 使用字节码操作显式生成桥接方法。我在这里不提供链接来阻止读者这样做。
于 2017-01-17T15:23:53.540 回答