java - 使具有相同删除二进制文件的通用返回类型兼容吗?

标签 java generics backwards-compatibility jls

我有以下类(class):

public abstract Foo {
  Foo() {}

  public abstract Foo doSomething();

  public static Foo create() {
    return new SomePrivateSubclassOfFoo();
  }
}

我想把它改成下面的定义:

public abstract Foo<T extends Foo<T>> {
  Foo() {}

  public abstract T doSomething();

  public static Foo<?> create() {
    return new SomePrivateSubclassOfFoo();
  }
}

此更改二进制兼容吗? 即,针对类的旧版本编译的代码是否可以在不重新编译的情况下与新版本一起使用?

我知道我需要更改 SomePrivateSubclassOfFoo,没关系。我也知道这个更改会在编译旧客户端代码时触发有关原始类型的警告,这对我来说也可以。我只是想确保不需要重新编译旧的客户端代码。

根据我的理解,这应该没问题,因为 T 的删除是 Foo,因此字节码中的 doSomething 的签名和以前一样。如果我查看 javap -s 打印的内部类型签名,我确实看到了这一点(尽管没有 -s 打印的“非内部”类型签名确实不同) . 我也确实对此进行了测试,它对我有用。

然而,Java API Compliance Checker告诉我这两个版本不是二进制兼容的。

那么什么是正确的呢? JLS 是否保证这里的二进制兼容性,或者我只是在测试中走运? (为什么会这样?)

最佳答案

是的,您的代码似乎没有破坏二进制兼容性。
我在爬取/阅读后发现了这些
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 上面写着:-

Adding or removing a type parameter of a class does not, in itself, have any implications for binary compatibility.
...
Changing the first bound of a type parameter of a class may change the erasure (§4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. The change of such a bound is analogous to the change of the first bound of a type parameter of a method or constructor (§13.4.13).

还有这个http://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_ones进一步阐明:-

According to the special compatibility story, the Java compiler treats a raw type as a reference to the type's erasure. An existing type can be evolved into a generic type by adding type parameters to the type declaration and judiciously introducing uses of the type variables into the signatures of its existing methods and fields. As long as the erasure looks like the corresponding declaration prior to generification, the change is binary compatible with existing code.

所以你现在没有问题,因为这是你第一次生成那个类。

但请记住,因为上面的文档也说:-

But, also bear in mind that there are severe constraints on how a type or method that already is generic can be compatibly evolved with respect to its type parameters (see the tables above). So if you plan to generify an API, remember that you only get one chance (release), to get it right. In particular, if you change a type in an API signature from the raw type "List" to "List<?>" or "List<Object>", you will be locked into that decision. The moral is that generifying an existing API is something that should be considered from the perspective of the API as a whole rather than piecemeal on a method-by-method or class-by-class basis.

所以我觉得,第一次做这个改变是可以的,但你只有一次机会,所以要充分利用它!

关于java - 使具有相同删除二进制文件的通用返回类型兼容吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42174045/

相关文章:

swift- 无法将类型 '[Int]' 的值转换为预期的参数类型 '[_]'

c++ - 未使用的私有(private)虚拟方法是否允许在不破坏 ABI 兼容性的情况下进行 future 扩展?

java - 在 openssl 的 native 代码中使用来自 java 的私钥

java - Android:创建自定义容器 View

swift - 专门化协议(protocol)继承的通用功能要求

ios - 如何针对随 SDK 变化的方法创建单元测试?

java - 我正在尝试转换 2 个使用 Java8 语言功能的模块,如何模仿静态/默认方法行为?

java - 选项卡式导航,将 Intent 附加信息传递给 fragment

java - OCPJP考试中的多线程

Java 泛型查询(上限通配符)