2017-04-19 103 views
3

我有一个公共的API,多次使用跨多个项目:我可以使用子接口重新编译公共API并保持二进制兼容性吗?

public interface Process<C extends ProcessExecutionContext> { 

    Future<?> performAsync(C context); 

} 

而且一个抽象类,负责实现未来的机制(未显示)。我知道,全部项目子类相应的抽象类(其中performAsync是最终)并没有单个类实现抽象接口没有子类抽象实现者。这是设计的,因为这个“公共”API在我们公司内是“公开”的。

查找有Future相比,Spring的ListenableFuture我决定将接口扩展到

public interface Process<C extends ProcessExecutionContext> { 

    ListenableFuture<?> performAsync(C context); 

} 

而且我已经在示例中未显示的一个抽象超类实现ListenableFuture过于限制。没有其他实现存在,通过设计。

到目前为止,每个呼叫者都使用Future这是一个超级接口ListenableFuture。如果您使用Future<?> future = processReturningListenable.performAsync(context),代码编译得很好。

问题是:如果我部署了公共API的跟上时代的JAR,含接口与ListenableFuture执行现有的环境,抽象超无需重新编译所有的项目,不performAsync调用仍然有效?

I.e.当Java被一个返回原始类型的子类型的方法替换时,它授予接口的二进制兼容性吗?

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

我假设我所要求的是可能的,因为Java方法名是由计数方法名称和输入参数的签名标识的。改变输出参数不改变方法名

+1

我不希望它在一个jar文件中有所作为 - 所以你应该可以用一个小应用程序(一个接口,一个实现,一个调用者;重新编译接口+实现,但不是来电者,看看它是否仍然有效)。 –

+1

@Jon Skeet:不需要测试;它不以那种方式工作。 – Holger

回答

2

这已经在The Java® Language Specification, §13. Binary Compatibility, §13.4.15. Method Result Type直接解决:

改变的方法的结果类型,或与void替换结果类型,或具有取代void结果类型,具有删除旧方法和添加新结果类型或新结果(请参阅§13.4.12)的新方法的组合效果。

引用的§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); 
} 

现在,Process继承LegacyProcess匹配的方法,并不需要导出一个类型,然后用更具体的返回类型将覆盖它,如你所愿。这被称为“协变式返回类型”。在字节码级别上,将会有一个“Future performAsync(…)”方法,它代表实际的实现方法“ListenableFuture performAsync(…)”。这种自动生成的委托方法被称为桥接方法

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

相关问题