【Practical API Design学习笔记】向后兼容

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,或者增加了某些功能,都可能会影响程序,使圆形变成不规则形状。

    也就是说,具体实现可能和预期差别很大,新版本的发布和旧版本差别也很大,就像阿米巴虫一样,形状变幻无穷。我们只能负上责任,尽量减少阿米巴虫效应。






转载于:https://my.oschina.net/tingzi/blog/133766

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值