Is the return type common with the same binary compatible erasure?

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 there be code that is compiled against the old version of the class working with the new version without republishing?

I know that I need to change SomePrivateSubclassOfFoo , this is normal. I also know that this change will cause warnings about raw types when compiling the old client code, this is also normal for me. I just want to make sure that the old client code does not need to be recompiled.

In my opinion, this should be fine, because erasing T is equal to Foo , and thus the doSomething signature in the byte code is the same as before. If I look at the internal caption labels printed by javap -s , I really see it confirmed (although the "non-internal" type javap -s printed without -s are different). I also checked this and it worked for me.

However, the Java API Compliance Checker tells me that the two versions are not binary compatible.

So what is right? Is JLS a guarantee of binary compatibility here, or was I just lucky in my tests? (Why could this happen?)

+7
java generics jls backwards-compatibility
source share
1 answer

Well yes, your code does not seem to violate binary compatibility.
I found them after some scanning / reading http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 which says: -

Adding or removing a class type parameter in and of itself has no implications for binary compatibility.
...
Changing the first boundary of a class type parameter can change the erasure (ยง4.6) of any member that uses this type parameter in its own type, and this can affect binary compatibility. Changing such a boundary is similar to changing the first boundary of a parameter of the type of a method or constructor (ยง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: -

According to a special version of compatibility, the Java compiler treats the source type as a reference to type erasure. An existing type can be converted to a general type by adding type parameters to the type declaration and intelligently introducing the use of type variables in the signature of its existing methods and fields. As long as erasure looks like a corresponding declaration before generation, this change is binary compatible with existing code.

Thus, you have no problems so far, as this is the first time you generate this class.

But please keep in mind , as the above document also says: -

But it should also be borne in mind that there are serious restrictions on how a type or method, which is already general, can be consistently developed in relation to its type parameters (see tables above). Therefore, if you plan to generate an API, remember that you have only one chance (release) so that everything is correct. In particular, if you change the type in the API signature from the raw List type to List <?>, Or List <Object>, you will be blocked in this solution. The moral is that generating an existing API is something that should be considered from the perspective of the API as a whole, and not in parts by method or class.

So, I think that everything is in order to make this change for the first time, but you have only one chance to fully use it!

+4
source share

All Articles