Is it possible to recompile an open API with a sub-interface and keep binaries compatible?

I have a public API that is used multiple times across multiple projects:

public interface Process<C extends ProcessExecutionContext> {

    Future<?> performAsync(C context);

}

      

And an abstract class that implements the Future mechanism (not shown). I know that the all subclass corresponds to the corresponding abstract class (for which performAsync is final ) and no class implements an abstract interface without subclassing the abstract constructor. This is by design and because this "public" API is "public" within our company.

Looking for something Future

too limited compared to Spring ListenableFuture

, I decided to extend the interface to

public interface Process<C extends ProcessExecutionContext> {

    ListenableFuture<?> performAsync(C context);

}

      

And I already implemented ListenableFuture in one abstract superclass not shown in the example. No other implementation exists, by design.

Every caller uses Future

which is a super interface ListenableFuture

. The code compiles well if you use Future<?> future = processReturningListenable.performAsync(context)

.

Question : If I deploy an updated public API JAR containing both an interface and an abstract superclass with implementation ListenableFuture

in existing environments, without recompiling all projects , does the call work performAsync

?

those. Does Java provide binary compatibility for interfaces when they are replaced with a method that returns a subtype of the original type?

I ask this because 1) I didn't find anyone available for a simple test with an existing JAR file and 2) recompile all projects, this is a red warning.

I guess what I'm asking is because Java method names are identified by a signature that counts the method name and input parameters. Changing out parameters does not change the method name

+3


source to share


1 answer


This is directly addressed in the Java® Language Specification, §13. Binary Compatibility, §13.4.15. Method result type :

Changing the result type of a method, or changing the result type to void

or replacing it void

with a result type, has the combined effect of removing the old method and adding a new method with a new result type or new result void

(see §13.4.12 ).

Referring to §13.4.12, saying:

...

Removing a method or constructor from a class can break compatibility with any pre-existing binary that referred to that method or constructor; a NoSuchMethodError

can be thrown when such a link from an existing binary is linked. Such an error will only occur if a method with a suitable signature and return type is not declared in the superclass.

So the answer is no, you can't do this without breaking binary compatibility with existing code.



Technically, it is simply wrong to assume that methods are identified only by name and parameter types, at the byte code level, they are always identified by name, parameter types and return type.

But note that the above message says, "This error will only be thrown if the superclass does not declare a method with a suitable signature and return type." This leads to a possible workaround:

interface LegacyProcess<C extends ProcessExecutionContext> {
    Future<?> performAsync(C context);
}
public interface Process<C extends ProcessExecutionContext> extends LegacyProcess<C> {
    @Override ListenableFuture<?> performAsync(C context);
}

      

Now Process

inherits the mapping method from LegacyProcess

, a type that you don't need to export, and then overrides it with a more specific return type as you wish. This is called the "Joint Variant Return Type". At the bytecode level, the " Future performAsync(…)

" method will be used , which will delegate the actual implementation method " ListenableFuture performAsync(…)

". This automatically generated delegation method is known as a bridging method.

Thus, the existing compiled client code continues to run, and each recompiled code will start using the new method directly without the bridge method.

+2


source







All Articles