简体   繁体   English

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

[英]Is making return type generic with same erasure binary compatible?

I have the following class: 我有以下课程:

public abstract Foo {
  Foo() {}

  public abstract Foo doSomething();

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

I want to change it to the following definition: 我想将其更改为以下定义:

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

  public abstract T doSomething();

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

Is this change binary compatible? 此更改二进制兼容吗? Ie, will code that is compiled against the old version of the class work with the new version without reocmpilation? 即,针对旧版本的类编译的代码是否可以使用新版本而无需重新复制?

I know that I need to change SomePrivateSubclassOfFoo , this is ok. 我知道我需要更改SomePrivateSubclassOfFoo ,这没关系。 I also know that this change will trigger warnings about raw types when old client code is compiled, this is also ok for me. 我也知道,当编译旧客户端代码时,此更改将触发有关原始类型的警告,这对我也是可以的。 I just want to make sure that old client code does not need to be recompiled. 我只是想确保不需要重新编译旧的客户端代码。

From my understanding, this should be ok because the erasure of T is Foo , and thus the signature of doSomething in the byte code is the same as before. 根据我的理解,这应该没问题,因为T的擦除是Foo ,因此字节码中doSomething的签名与之前相同。 If I look at the internal type signatures printed by javap -s , I indeed see this confirmed (although the "non-internal" type signatures printed without -s do differ). 如果我查看由javap -s打印的内部类型签名,我确实看到了这一点(虽然没有-s打印的“非内部”类型签名确实不同)。 I also did test this, and it worked for me. 我也测试了这个,它对我有用。

However, the Java API Compliance Checker tells me that the two versions are not binary compatible. 但是, Java API Compliance Checker告诉我这两个版本不是二进制兼容的。

So what is correct? 什么是正确的? Does the JLS guarantee binary compatibility here, or was I just lucky in my tests? JLS是否保证二进制兼容性,或者我在测试中是否幸运? (Why could this happen?) (为什么会发生这种情况?)

Well yes your code does not seem to break binary compatibility. 好吧,是的,您的代码似乎没有打破二进制兼容性。
I found these after some crawling/reading 一些爬行/阅读后我发现了这些
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 which says:- 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. 更改类的类型参数的第一个边界可能会更改在其自己的类型中使用该类型参数的任何成员的擦除(第4.6节),这可能会影响二进制兼容性。 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). 这种绑定的更改类似于方法或构造函数的类型参数的第一个边界的更改(第13.4.13节)。

And this http://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_ones further clarifies:- 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. 根据特殊兼容性故事,Java编译器将原始类型视为对类型擦除的引用。 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. 只要擦除在泛化之前看起来像相应的声明,该更改就与现有代码二进制兼容。

So you have no problems as of now since it is the first time you are generifying that class. 因此,你现在没有任何问题,因为这是你第一次将这个课程弄清楚。

But please keep in mind as the above doc also says :- 但请记住 ,上述文件也说: -

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. 因此,如果您计划生成API,请记住您只有一次机会(发布),以使其正确。 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. 特别是,如果您将API签名中的类型从原始类型“List”更改为“List <?>”或“List <Object>”,您将被锁定在该决定中。 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. 道德观点认为,对API进行整合应该从API整体的角度考虑,而不是逐个方法或逐个类地逐个考虑。

So I think, its alright to make this change for the very first time but you have one chance only so make full use of it! 所以我认为,这是第一次做出这一改变,但你只有一次机会才能充分利用它!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM