为了账号安全,请及时绑定邮箱和手机立即绑定

源兼容性是否总是意味着二进制兼容性?

源兼容性是否总是意味着二进制兼容性?

ITMISS 2023-07-19 16:46:31
假设我编译我的类并lib_v1.0.jar分发我的二进制文件。然后我更新到lib_v1.1.jar. 假设我的源代码针对 v1.1 进行编译而没有错误,那么我的旧分布式二进制文件是否始终可以使用lib_v1.1.jar,或者我是否必须重新编译和重新分发新的二进制文件?
查看完整描述

4 回答

?
烙印99

TA贡献1829条经验 获得超13个赞

源兼容性并不意味着二进制兼容性。


例如,我们在库中有以下接口:


// jar v1.0 

interface Service {

   int calculate(); // signature: ()I

}

我们从代码中调用它:


Service service = ...

int a = service.calculate(); // calls calculate()I method

在版本 v1.1 中,库代码略有更改:


// jar v1.1 

interface Service {

   Integer calculate(); // signature: ()Ljava.lang.Integer;

}

由于自动装箱,源代码仍然兼容,但是如果我们使用新的库版本运行代码,它将失败并出现 java.lang.NoSuchMethodError: Service.calculate()I 错误。


类似的问题可能是由 lambda 作用域、泛型转换、隐式类型转换等引起的。


查看完整回答
反对 回复 2023-07-19
?
撒科打诨

TA贡献1934条经验 获得超2个赞

假设我的源代码针对 v1.1 进行编译且没有错误,我的旧分布式二进制文件是否始终可以工作

你应该相对安全,只要你的代码不使用反射,例如通过名称实例化类,或者非常糟糕,通过“后门”访问私有字段。当然,反射都是在运行时发生的,所以“我的使用反射的代码仍然可以编译”对您没有任何帮助。或者如图所示,只要第 3 方 API 中的更改不是关于编译器将优雅地忽略的签名更改,例如将 int 转换为 Integer,如其他答案中所述。

话虽如此:真正的问题不是“它编译”。真正的问题是:它仍然按预期运行吗?

而这个问题无法通过编译来回答。

因此,让我们增强您的陈述:

假设我的源代码针对 v1.1 进行编译而没有错误,并且我编写的所有单元和集成测试都是为了确保我的代码在针对 v1.1 运行时能够正确地让第 3 方库全部通过...

那么你就可以非常安全了。除非你忘记了一些重要的测试用例。

但基本上,无论您的产品使用 v1.0 还是 v1.1,您对产品的质量都有类似的信心。因为即使使用 v1.0,您仍然可能会遇到明天会出现关于您以前从未见过的问题的错误报告,只是因为这种情况很少发生。


查看完整回答
反对 回复 2023-07-19
?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

如果库的目标 jdk 与用于编译类的目标 jdk 兼容(假设新库中没有重大更改),那么它应该可以工作。



查看完整回答
反对 回复 2023-07-19
?
一只名叫tom的猫

TA贡献1906条经验 获得超2个赞

不,不是的。使用更改后的常量将是不正确的。


考虑库中的这个常量字段:


package com.example.lib;


public class LibConstants {

    public static final int MAX_THREADS = 8;

}

Java 编译器在编译时替换常量。 如果您的代码如下所示:


someLibClass.startServer(LibConstants.MAX_THREADS);

Java 会将其编译为:


someLibClass.startServer(8);

如果库的更高版本更改了该常量:


public class LibConstants {

    public static final int MAX_THREADS = 20;

}

您的代码将不会使用常量的新值,因为编译器在编译代码时删除了对静态字段的任何引用。


查看完整回答
反对 回复 2023-07-19
  • 4 回答
  • 0 关注
  • 84 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信