是否使用相同的擦除二进制兼容返回类型泛型?

Phi*_*ler 7 java generics backwards-compatibility jls

我有以下课程:

public abstract Foo {
  Foo() {}

  public abstract Foo doSomething();

  public static Foo create() {
    return new SomePrivateSubclassOfFoo();
  }
}
Run Code Online (Sandbox Code Playgroud)

我想将其更改为以下定义:

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

  public abstract T doSomething();

  public static Foo<?> create() {
    return new SomePrivateSubclassOfFoo();
  }
}
Run Code Online (Sandbox Code Playgroud)

此更改二进制兼容吗?即,针对旧版本的类编译的代码是否可以使用新版本而无需重新复制?

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

从我的理解,这应该是好的,因为擦除TFoo,这样的签名doSomething在字节码是和以前一样.如果我看一下印刷的内部类型签名javap -s,我确实看到了这个确认(虽然打印的"非内部"类型签名没有-s做不同).我也测试了这个,它对我有用.

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

什么是正确的?JLS是否保证二进制兼容性,或者我在测试中是否幸运?(为什么会发生这种情况?)

Shu*_*sia 4

嗯,是的,您的代码似乎没有破坏二进制兼容性。
\n我在爬行/阅读
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5后发现了这些\n其中说:-

\n\n
\n

添加或删除类的类型参数本身不会对二进制兼容性产生任何影响。\n
\n ...\n
\n 更改类的类型参数的第一个边界可能会更改擦除(\ xc2\xa74.6) 在其自己的类型中使用该类型参数的任何成员,这可能会影响二进制兼容性。这种边界的更改类似于方法或构造函数的类型参数的第一个边界的更改 (\xc2\xa713.4.13)。

\n
\n\n

这个http://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_ones进一步澄清:-

\n\n
\n

根据特殊的兼容性故事,Java 编译器将原始类型视为对该类型擦除的引用。通过将类型参数添加到类型声明中并明智地将类型变量的使用引入其现有方法和字段的签名中,现有类型可以演变成泛型类型。只要擦除看起来像泛化之前的相应声明,该更改就与现有代码二进制兼容。

\n
\n\n

所以到目前为止你没有任何问题,因为这是你第一次泛化该类。

\n\n

但请记住,上面的文档还说:-

\n\n
\n

但是,还要记住,已经是泛型的类型或方法如何与其类型参数兼容地演化存在严格的限制(参见上表)。因此,如果您计划通用化 API,请记住您只有一次机会(发布)才能正确完成它。特别是,如果您将 API 签名中的类型从原始类型“List”更改为“List<?>”或“List<Object>”,您将被锁定在该决定中。其寓意是,泛化现有的 API 应该从 API 的整体角度来考虑,而不是逐个方法或逐个类地零散地考虑。

\n
\n\n

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

\n