1、源代码兼容
如果程序用java1.3可以编译通过,用java1.4也可以编译通过,那么我们说java代码是源代码兼容的。但是这样是很难的。
例如:
public class WrappingIOException extends IOException {
private IOException cause;
public WrappingIOException(IOException cause) {
this.cause = cause;
}
public IOException getCause() {
return cause;
}
}
以上代码在java1.3可以编译通过,但是java1.4不能编译通过。因为java1.4的IOException的超类中引入了getCause()方法,返回值是Throwable类型,java1.4中不允许覆盖方法名字相同、参数相同,但是返回值不同的方法。因此,会导致编译失败。但是java1.5是会编译成功了,因为java改进了。
引入子类是危险的,引入其他非子类也是危险的。
例如:
import java.awt.*;
import java.util.*;
/** Could be compiled on JDK 1.2, before java.util.List was created */
public class VList extends List {
Vector v;
}
上面代码在java1.1中是可以编译通过的,因为java1.1中有java.awt.List。但是在java1.2中是编译不过的,因为java1.2中增加了java.util.List类,这时编译器不知道用的是java.awt.List还是java.util.List。这就是增加新类导致源代码不兼容。
这种情况应该如何处理呢?放弃源代码兼容是更好的选择。因为,开发者使用时,只需要将通配符*改了,然后重新编译即可,这样做并不会很麻烦。保证源代码兼容也需要折中的选择。
2、二进制兼容
我们利用一个库来开发一个程序,如果这个库换成新的库了,而我们的程序不需要重新编译,那么就叫二进制兼容。
二进制兼容与源代码兼容的区别:
在Class文件中,名称都是全名,因此,在java文件中的通配符*导入方式在Class文件中是不存在的。在Class文件中,字段、方法名、参数、返回值用的都是全名,就是说前面都会加类名。因此,像下面这段代码,方法名、参数相同,但是返回值不同,在java代码中是编译不通过的,但是在Class文件中是可以这样写的。
/** This is not Java code, but it is fine inside the bytecode */
public abstract class DoubleReturnType {
public abstract String getName(int x);
public abstract StringBuffer getName(int x);
}
这是java代码和二进制代码最大的不同。
在访问一个字段时,会在该字段的类中找该字段;在访问一个方法时,那么不仅在指定类中找,还会在其他所有的父类中查找。所以相比使用字段,最好还是使用方法,以便未来改进。
看下面的代码,Impl实现了API抽象类,并在init方法中队version进行了判断。
public abstract class API {
public static final int VERSION = 1;
protected API() {
System.err.println("Initializing version " + VERSION);
init(API.VERSION);
System.err.println("Properly initialized: " + this);
}
protected abstract void init(int version) throws IllegalStateException;
}
public class Impl extends API {
protected void init(int version) throws IllegalStateException {
if (version != API.VERSION) {
throw new IllegalStateException("Wrong API version error!");
}
}
}
为什么在init中对version进行判断呢,因为这两个值并不是总是相等的,只有在Impl和API这两个类同时编译时才是相等的。比如:写好了Impl类,这时version值改成版本2了,而Impl并没有重新编译,这时就不相等了,以为在之前一次编译Impl时,Impl的Class文件已经记录了版本1。
3、功能兼容——阿米巴虫效应
对于我们的应用,我们一开始看起来是这样的:
但是现实的功能应该是这样的,开发的API不可能完全按照规范,或者是少了些功能,或者是多了些功能:
而开发人员在使用API时,基本不会根据文档中所规定的去做,基本会用“编译/运行”的方式来完成自己的工作。他们关注的是API的功能,并不关心规范怎么说。这样的话,如果API的实现少了些功能,或者多了些功能,这样都是危险的。对于新版本发布,修复了某些bug,或者增加了某些功能,都可能会影响程序,使圆形变成不规则形状。
也就是说,具体实现可能和预期差别很大,新版本的发布和旧版本差别也很大,就像阿米巴虫一样,形状变幻无穷。我们只能负上责任,尽量减少阿米巴虫效应。