理解二进制兼容的要害是要理解延迟绑定(Late Binding)。延迟绑定是指Java直到运行时才检查类、域、方法的名称,而不象C/C++的编译器那样在编译期间就清除了类、域、方法的名称,代之以偏移量数值——这是Java二进制兼容得以发挥作用的要害。
由于采用了延迟绑定技术,方法、域、类的名称直到运行时才解析,意味着只要域、方法等的名称(以及类型)一样,类的主体可以任意替换——当然,这是一种简化的说法,还有其他一些规则制约Java类的二进制兼容性,例如访问属性(private、public等)以及是否为abstract(假如一个方法是抽象的,那么它肯定是不可直接调用的)等,但延迟绑定机制无疑是二进制兼容的核心所在。
只有把握了二进制兼容的规则,才能在改写类的时候保证其他类不受到影响。下面再来看一个例子,FrodoMail和SamMail是两个Email程序:
abstract class Message implements Classifiable { }
class EmailMessage extends Message {
public boolean isJunk() { return false; }
}
interface Classifiable {
boolean isJunk();
}
class FrodoMail {
public static void main(String a[]) {
Classifiable m = new EmailMessage();
System.out.println(m.isJunk());
}
}
class SamMail {
public static void main(String a[]) {
EmailMessage m = new EmailMessage();
System.out.println(m.isJunk());
}
}
假如我们重新实现Message,不再让它实现Classifiable接口,SamMail仍能正常运行,但FrodoMail会抛出异常:java.lang.IncompatibleClassChangeError at FrodoMail.main。这是因为SamMail不要求EmailMessage是一个Classifiable,但FrodoMail却要求EmailMessage是一个Classifiable,编译FrodoMail得到的二进制.class文件引用了Classifiable这个接口名称。符合Classifiable接口定义的方法仍然存在,但该类却根本没有提到Classifiable这个接口。