本文介绍了我可以重新编译带有子接口的公共API并保持二进制兼容性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

我有一个公共API,在多个项目中多次使用:

I have a public API, used several times across several projects:

public interface Process<C extends ProcessExecutionContext> {

    Future<?> performAsync(C context);

}

还有一个抽象类,负责实现Future机制(未显示).我知道 all 项目子类化了相应的抽象类(对于它们来说,performAsync是 final ),没有一个子类可以实现抽象接口而不对抽象实现者进行子类化.这是设计使然,因为此公共" API在我们公司内部是公共".

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

发现Future与Spring的ListenableFuture相比过于局限,因此我决定将接口扩展为

Finding that Future is too limitative compared to Spring's ListenableFuture I decided to extend the interface to

public interface Process<C extends ProcessExecutionContext> {

    ListenableFuture<?> performAsync(C context);

}

并且我已经在示例中未显示的单个抽象超类中实现了ListenableFuture.设计上没有其他实现方式.

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

到目前为止,每个调用方都使用Future,它是ListenableFuture的超级接口.如果使用Future<?> future = processReturningListenable.performAsync(context),则代码可以很好地编译.

Every caller so far uses Future which is a superinterface of ListenableFuture. Code compiles well if you use Future<?> future = processReturningListenable.performAsync(context).

问题是:如果我部署公共API的最新JAR,同时包含接口以及具有ListenableFuture实现的抽象超类,同时包含 在没有重新编译所有项目的情况下performAsync调用仍然有效吗?

Question is: if I deploy an up-to-date JAR of the public API, containing both the interface and the abstract superclass with ListenableFuture implementation to existing environments, without recompiling all the projects, does the performAsync call still work?

即将接口替换为返回原始类型的子类型的方法时,Java如何授予接口的二进制兼容性?

I.e. does Java grant binary compatibility of interfaces when they are replaced with a method that return a subtype of the original type?

我之所以这样问,是因为1)我发现没有人可以使用现有的JAR文件进行简单测试,并且2)必须重新编译所有项目才是红色警报.

I am asking this because 1) I find no one available for doing a simple test with an existing JAR file and 2) having to recompile all projects is a red alert.

我假设我要问的是可能的,因为Java方法名称是由对方法名称和输入参数进行计数的签名标识的.更改输出参数不会更改方法的名称

I assume what I ask is possible because Java method names are identified by a signature that counts the method name and the input parameters. Changing the output parameters doesn't change the method's name

推荐答案

此问题已直接在《Java®语言规范》第13节.二进制兼容性,第1.3.4.15节.方法结果类型:

引用的§13.4.12说:

The referenced §13.4.12 saying:

从类中删除方法或构造函数可能会破坏与任何引用该方法或构造函数的现有二进制文件的兼容性;当链接来自现有二进制文件的引用时,可能会抛出NoSuchMethodError.仅当在超类中没有声明具有匹配的签名和返回类型的方法时,才会发生这种错误.

Deleting a method or constructor from a class may break compatibility with any pre-existing binary that referenced this method or constructor; a NoSuchMethodError may be thrown when such a reference from a pre-existing binary is linked. Such an error will occur only if no method with a matching signature and return type is declared in a superclass.

因此,答案是,不,您必须这样做,否则可能会破坏与现有代码的二进制兼容性.

So the answer is, no, you can’t do that without potentially breaking binary compatibility with existing code.

从技术上讲,假设方法仅由名称和参数类型标识,在字节码级别上,它们始终由名称,参数类型返回类型标识是完全错误的.

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

但是请注意,上面的引用指出"仅当在超类中没有声明具有匹配的签名和返回类型的方法时,才会发生这种错误".这指导了一种可能的解决方法:

But note that the cite above states "Such an error will occur only if no method with a matching signature and return type is declared in a superclass". This guides to a possible work-around:

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

现在,Process继承了LegacyProcess的匹配方法,该方法不需要导出,然后根据需要使用更具体的返回类型覆盖它.这称为协变量返回类型".在字节码级别上,将存在一个"Future performAsync(…)"方法,该方法委派给实际的实现方法"ListenableFuture performAsync(…)".这种自动生成的委派方法称为 bridge方法.

Now, Process inherits a matching method from LegacyProcess, a type that doesn’t need to be exported, then overrides it with the more specific return type, as you wish. This is called "co-variant return type". On the byte code level, there will be a "Future performAsync(…)" method which delegates to the actual implementation method "ListenableFuture performAsync(…)". This automatically generated delegation method is known as bridge method.

这样,现有的已编译客户端代码将继续起作用,而每个重新编译的代码将直接使用新方法开始,而无需使用桥方法.

That way, existing compiled client code continues to work, while every recompiled code will start using the new method directly without the bridge method.

这篇关于我可以重新编译带有子接口的公共API并保持二进制兼容性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-06 22:41