Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我可以用子接口重新编译公共API并保持二进制兼容性吗?_Java_Oop_Binary Compatibility_Jvm Bytecode - Fatal编程技术网

Java 我可以用子接口重新编译公共API并保持二进制兼容性吗?

Java 我可以用子接口重新编译公共API并保持二进制兼容性吗?,java,oop,binary-compatibility,jvm-bytecode,Java,Oop,Binary Compatibility,Jvm Bytecode,我有一个公共API,在多个项目中多次使用: public interface Process<C extends ProcessExecutionContext> { Future<?> performAsync(C context); } 我已经在示例中未显示的单个抽象超类中实现了ListenableFuture。根据设计,不存在其他实现 到目前为止,每个调用方都使用Future,这是ListenableFuture的超级接口。如果使用Future-Fut

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

public interface Process<C extends ProcessExecutionContext> {

    Future<?> performAsync(C context);

}
我已经在示例中未显示的单个抽象超类中实现了ListenableFuture。根据设计,不存在其他实现

到目前为止,每个调用方都使用
Future
,这是
ListenableFuture
的超级接口。如果使用Future-Future=processReturningListenable.performAsync(上下文),代码编译效果很好

问题是:如果我部署了一个最新的公共API JAR,其中包含接口和抽象超类,并在现有环境中实现了
ListenableFuture
而不重新编译所有项目,那么
performAsync
调用仍然有效吗

也就是说,当用返回原始类型的子类型的方法替换接口时,Java是否授予接口的二进制兼容性

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


我假设我所问的是可能的,因为Java方法名由一个计数方法名和输入参数的签名标识。更改输出参数不会更改方法的名称

这已在中直接解决:

更改方法的结果类型,或将结果类型替换为
void
,或将
void
替换为结果类型,具有删除旧方法并使用新结果类型或新的
void
结果添加新方法的组合效果(请参阅)

引用的§13.4.12规定:

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

因此,答案是,不,如果不破坏与现有代码的二进制兼容性,就无法做到这一点

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

但请注意,上面的引用声明“只有在超类中没有声明具有匹配签名和返回类型的方法时,才会发生这种错误”。这将指导可能的解决方法:

interface LegacyProcess<C extends ProcessExecutionContext> {
    Future<?> performAsync(C context);
}
public interface Process<C extends ProcessExecutionContext> extends LegacyProcess<C> {
    @Override ListenableFuture<?> performAsync(C context);
}
接口进程{
未来性能同步(C上下文);
}
公共接口过程扩展了LegacyProcess{
@重写ListenableFuture性能同步(C上下文);
}
现在,
Process
LegacyProcess
继承了一个匹配方法,一个不需要导出的类型,然后按照您的意愿用更具体的返回类型覆盖它。这称为“共变量返回类型”。在字节码级别,将有一个“
未来性能同步(…)
”方法,该方法将委托给实际的实现方法“
ListenableFuture性能同步(…)
”。这种自动生成的委托方法称为桥接方法


这样,现有的已编译客户端代码将继续工作,而每一个重新编译的代码都将开始直接使用新方法,而不使用桥接方法。

我不认为它在jar文件中会有任何区别-因此,您应该能够很容易地用一个小应用程序测试这一点(一个接口,一个实现,一个调用者;重新编译接口+实现,但不编译调用者,看看它是否仍然有效)。@Jon Skeet:不需要测试;它不是那样工作的。
interface LegacyProcess<C extends ProcessExecutionContext> {
    Future<?> performAsync(C context);
}
public interface Process<C extends ProcessExecutionContext> extends LegacyProcess<C> {
    @Override ListenableFuture<?> performAsync(C context);
}