我正在阅读约书亚布洛赫的Effective Java。
在第17项:“仅使用接口定义类型”中,我遇到了不建议使用接口存储常量的解释。我在下面解释。
“更糟糕的是,它代表了一种承诺:如果在将来的版本中,该类被修改以便它 不再需要使用常量,它仍然必须实现接口以确保二进制 兼容性“。
二进制兼容性在这里意味着什么?
有人可以用Java中的示例指导我,以证明代码是二进制兼容的。
答案 0 :(得分:32)
简而言之,二进制兼容性意味着当您更改类时,不需要重新编译使用它的类。例如,您从此类中删除或重命名了公共或受保护的方法
public class Logger implements Constants {
public Logger getLogger(String name) {
return LogManager.getLogger(name);
}
}
从您的log-1.jar库中发布了一个新版本作为log-2.jar。当您的log-1.jar的用户下载新版本时,当他们尝试使用缺少的getLogger(String name)方法时,它将破坏他们的应用程序。
如果删除常量接口(第17项),由于相同的原因,这将破坏二进制兼容性。
但是您可以删除/重命名此类的私有或包私有成员,而不会破坏二进制兼容性,因为外部应用程序不能(或不应该)使用它。
答案 1 :(得分:17)
为了更好地理解这个概念,有趣的是,二进制兼容性并不意味着API兼容性,反之亦然。
API兼容但非二进制兼容:静态删除
图书馆第1版:
public class Lib {
public static final int i = 1;
}
客户代码:
public class Main {
public static void main(String[] args) {
if ((new Lib()).i != 1) throw null;
}
}
使用版本1编译客户端代码:
javac Main.java
将版本1替换为版本2:删除static
:
public class Lib {
public final int i = 1;
}
重新编译只版本2,不客户端代码,然后运行java Main
:
javac Lib.java
java Main
我们得到:
Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field Lib.i
at Main.main(Main.java:3)
这是因为即使我们可以在Java中为(new Lib()).i
和成员方法编写static
,它也会根据Lib
编译为两个不同的VM指令:getstatic
或getfield
。这个中断在JLS 7 13.4.10:
如果未声明为private的字段未声明为static且更改为声明为static,反之亦然,则如果该字段由预先存在的二进制文件使用,则将导致链接错误,特别是IncompatibleClassChangeError。期待另一种领域。
我们需要使用Main
重新编译javac Main.java
才能使用新版本。
注意:
(new Lib()).i
这样的类实例调用静态成员是不好的风格,引发警告,永远不应该完成final
原语是无用的:总是使用static final
原语:private final static attribute vs private final attribute 二进制兼容但不兼容API:null前置条件强化
版本1:
public class Lib {
/** o can be null */
public static void method(Object o) {
if (o != null) o.hashCode();
}
}
第2版:
public class Lib {
/** o cannot be null */
public static void method(Object o) {
o.hashCode();
}
}
客户端:
public class Main {
public static void main(String[] args) {
Lib.method(null);
}
}
这一次,即使在更新Main
后重新编译Lib
,第二次调用也会抛出,但不是第一次调用。
这是因为我们以一种在Java编译时无法检查的方式更改了method
的契约:在null
之后,不再是null
。
注意:
C二进制兼容性示例
答案 2 :(得分:6)
二进制兼容性
Java二进制兼容性规定了条件 哪些修改和重新编译类 没有必要重新编译进一步的类导入 - 修改过的课程。二进制兼容性是一种新颖的 语言设计的概念。
Java language specication [7]描述了二进制文件 适用的变化如下:
对类型的更改是二进制兼容的 (等效地,不会破坏兼容性) 预先存在的二进制文件(如果已存在的二进制文件) 以前链接没有错误将con 可以毫无错误地链接。
答案 3 :(得分:0)
如果将来我们希望更改某些类正在实现的接口(例如,添加一些新方法)。
如果我们添加抽象方法(其他方法),那么类(实现接口) 必须实现 创建依赖性约束的附加方法和执行相同的成本开销。
为了解决这个问题,我们可以在界面中添加默认方法。
这将删除实现其他方法的依赖关系。
我们不需要修改实现类来合并更改。 这称为二进制兼容性。
请参考以下示例:
我们将要使用的界面
//Interface
interface SampleInterface
{
// abstract method
public void abstractMethod(int side);
// default method
default void defaultMethod() {
System.out.println("Default Method Block");
}
// static method
static void staticMethod() {
System.out.println("Static Method Block");
}
}
//The Class that implements the above interface.
class SampleClass implements SampleInterface
{
/* implementation of abstractMethod abstract method, if not implemented
will throw compiler error. */
public void abstractMethod(int side)
{System.out.println(side*side);}
public static void main(String args[])
{
SampleClass sc = new SampleClass();
sc.abstractMethod(4);
// default method executed
sc.defaultMethod();
// Static method executed
SampleInterface.staticMethod();
}
}
注意:有关详细信息,请参阅default methods
答案 4 :(得分:-1)
让事情变得简单:
可以运行与另一台计算机上运行相同的二进制代码的计算机被认为是二进制兼容的。这与源代码兼容性不同,后者可能需要重新编译。
二进制兼容性是开发要在多个操作系统上运行的计算机程序的主要好处。