[英]Can I recompile a public API with a sub-interface and keep binary compatibility?
I have a public API, used several times across several projects: 我有一个公共API,在几个项目中多次使用:
public interface Process<C extends ProcessExecutionContext> {
Future<?> performAsync(C context);
}
And an abstract class that takes care of implementing the Future mechanism (not shown). 还有一个抽象类,负责实现Future机制(未显示)。 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.
我知道所有项目都是相应的抽象类的子类(对于这些抽象类,performAsync是最终的 ),并且没有单个类在没有子类化抽象实现者的情况下实现抽象接口。 This is by design and because this "public" API is "public" within our company.
这是设计上的,因为这个“公共”API在我们公司内是“公开的”。
Finding that Future
is too limitative compared to Spring's ListenableFuture
I decided to extend the interface to 与Spring的
ListenableFuture
相比,发现Future
太有限了我决定将界面扩展到
public interface Process<C extends ProcessExecutionContext> {
ListenableFuture<?> performAsync(C context);
}
And I already implemented the ListenableFuture in the single abstract superclass not shown in the example. 我已经在示例中未显示的单个抽象超类中实现了ListenableFuture。 No other implementation exists, by design.
根据设计,没有其他实现存在。
Every caller so far uses Future
which is a superinterface of ListenableFuture
. 到目前为止,每个调用者都使用
Future
,它是ListenableFuture
的超级ListenableFuture
。 Code compiles well if you use Future<?> future = processReturningListenable.performAsync(context)
. 如果使用
Future<?> future = processReturningListenable.performAsync(context)
代码编译得很好。
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? 问题是:如果我部署的公共API的跟上时代的JAR,包含两种接口,并与抽象超
ListenableFuture
落实到现有环境, 而无需重新编译所有的项目 ,并在performAsync
电话仍然有效?
Ie does Java grant binary compatibility of interfaces when they are replaced with a method that return a subtype of the original type? 即,当接口被返回原始类型的子类型的方法替换时,Java是否授予接口的二进制兼容性?
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. 我问这个是因为1)我发现没有人可以使用现有的JAR文件进行简单测试,2)必须重新编译所有项目是一个红色警报。
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. 我假设我的要求是可能的,因为Java方法名称由一个计算方法名称和输入参数的签名来标识。 Changing the output parameters doesn't change the method's name
更改输出参数不会更改方法的名称
This has been directly addressed in The Java® Language Specification, §13. 这已在Java®语言规范§13中直接解决。 Binary Compatibility, §13.4.15.
二进制兼容性,§13.4.15。 Method Result Type :
方法结果类型 :
Changing the result type of a method, or replacing a result type with
void
, or replacingvoid
with a result type, has the combined effect of deleting the old method and adding a new method with the new result type or newlyvoid
result (see §13.4.12 ).改变的方法的结果类型,或与替换结果类型
void
,或替换void
与结果类型,具有删除旧方法和添加新方法与新的结果类型的或新的组合效果void
结果(见§ 13.4.12 )。
The referenced §13.4.12 saying: 引用的§13.4.12说:
…
...
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.当链接来自预先存在的二进制文件的此类引用时,可能会抛出
NoSuchMethodError
。 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);
}
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. 现在,
Process
继承了LegacyProcess
的匹配方法,这是一种不需要导出的类型,然后根据需要使用更具体的返回类型覆盖它。 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(…)
”. 在字节代码级别,将有一个“
Future performAsync(…)
”方法,该方法委托给实际的实现方法“ 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. 这样,现有的编译客户端代码继续工作,而每个重新编译的代码将直接使用新方法而不使用桥接方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.