首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java二进制兼容性- RFC提出的使用invokevirtual的协变量返回类型的解决方案

Java二进制兼容性- RFC提出的使用invokevirtual的协变量返回类型的解决方案
EN

Stack Overflow用户
提问于 2010-08-17 06:53:35
回答 3查看 309关注 0票数 3

我正在尝试开发一个API。作为这种演变的一部分,我需要将方法的返回类型更改为子类(专门化),以便高级客户端能够访问新功能。例子(忽略丑陋的:

代码语言:javascript
复制
public interface Entity {
  boolean a();
}

public interface Intf1 {
  Entity entity();
}

public interface Main {
  Intf1 intf();
}

我现在想让ExtendedEntity、Intf2和Main这样的:

代码语言:javascript
复制
public interface ExtendedEntity extends Entity {
  boolean b();
}

public interface Intf2 extends Intf1 {
  ExtendedEntity entity();
}

public interface Main {
  Intf2 intf();
}

但是,由于方法返回类型是其签名的一部分,因此已经用以前版本的代码编译的客户端会显示链接错误(方法未找到iirc)。

我想做的是用不同的返回类型向Main添加一个方法。两个方法(一个返回父类型,另一个返回子类型)应该映射到相同的实现方法(该方法返回子类型)。注意-据我所知,JVM允许这样做,但Java规范不允许这样做。

我的解决方案,这似乎是滥用(我没有其他词)的Java类系统添加所需的接口。

代码语言:javascript
复制
public interface Main_Backward_Compatible {
  Intf1 intf();
}

public interface Main extends Main_Backward_Compatible{
  Intf2 intf();
}

现在,旧客户端将获得返回到invokevirtual的正确方法(因为具有正确返回类型的方法存在于类型层次结构中),而实际工作的实现将是返回子类型Intf2的实现。

这似乎很管用。在我能设计的所有测试中(除了反射--但我不在乎那个部分),它确实奏效了。

它永远都能用吗?我的推理(关于发票虚拟)正确吗?

还有另一个相关的问题--是否有检查“真实”二进制兼容性的工具?我发现的唯一方法是单独查看每个方法,但没有考虑类型层次结构。

谢谢,

跑了。

编辑--我尝试过的工具“不太好”(不要考虑类型层次结构):

  1. 克里尔0.6
  2. IntelliJ "APIComparator“插件。

Edit2 --当然,我的客户端被禁止为我的接口(想想服务)创建实现类。但是,如果您想要完整的示例,请考虑抽象类(对于Main)而不是接口。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-05-26 13:47:46

我们最终不需要解决方案,但在此之前证明了它的有效性。

票数 1
EN

Stack Overflow用户

发布于 2010-08-17 14:25:07

这足够长,我承认我没有仔细阅读所有的东西,但似乎你可能真的想在这里利用泛型。如果键入Intf1,我认为可以在引入专门化的同时保持二进制兼容性:

代码语言:javascript
复制
public interface Intf1<T extends Entity> {
  T entity(); //erasure is still Entity so binary compatibility
}

public interface Intf2 extends Intf1<ExtendedEntity> { //if even needed
}

public interface Main {
  Intf1<ExtendedEntity> intf(); //erasure is still Intf1, the raw type
}

编辑#1:在试图维护二进制兼容性时有一些注意事项。有关详细信息,请参阅泛型教程第6章和第10章。

编辑#2:

您也可以将这个概念扩展到键入Main

代码语言:javascript
复制
public interface Main<T, I extends Intf1<T>> {
    I intf(); //still has the same erasure as it used to, so binary compatible
}

然后,旧客户端将能够像以前一样使用原始主类型,而不需要重新编译,新客户端将键入它们对Main的引用:

代码语言:javascript
复制
Main<ExtendedEntity, Intf2> myMain = Factory.getMeAMain();
Intf2 intf = myMain.intf();
票数 1
EN

Stack Overflow用户

发布于 2010-08-17 14:14:27

如果根本不改变现有的接口,就更简单了。任何使用您的新界面的人都将编写新代码。

现有Main.intf()签名的实现可以返回Intf2的实例。

或者,您可以提供不需要强制转换的新访问器:

代码语言:javascript
复制
public interface Main2 extends Main {
  Intf2 intf2();
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3499981

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档