【笔记20】接口优于抽象类

接口和抽象用来定义允许多个实现的类型。

两种机制之间最明显的区别在:① 抽象类允许包含某个方法的实现,而接口则不允许。②为了实现由抽象类定义的类型,类必须成为抽象类的一个子类。Java只允许单继承,所以,抽象类作为类型定义受到了极大限制。

接口的优点:

  • 现在的类可以很容易的被更新,以实现新的接口。 如果这些方法尚不存在,你需要做的就只是增加必要的方法,然后在类的声明中增加一个implement子句。比如有一个类A,后期有在系统中加入了一个新的接口B,当你想让类A来实现接口B,你只用加上一句implement B,然后在类A中实现里面的方法即可,不会影响到以前的类对类A的使用。
  • 接口定义mixin(混合类型)的理想选择。 不严格的来讲,mixin是指这样的类型:类除了实现它的“基本类型”之外,还可以实现这个mixin类型,以表明它提供了某些可选择的行为。意思是,一个类可以实现多个接口。一个类除了可以实现一个它的主要类型接口之外,还可以加入一些辅助接口来实现一些新的功能。
  • 接口允许我们构造非层次结构的类型框架。类型层次对于组织某些事物是非常合适的。但是其他有些事物并不能被整齐地组织成一个严格的层次结构。

骨架类

骨架实现(skeletal implementation)类,把接口和抽象类的优点结合起来。接口的作用仍然是定义类型,但是骨架实现类接管了所有域接口实现相关的工作。骨架类的做法是用一个抽象类来实现一个接口,在抽象类中为接口的某些方法提供实现。

按照惯例,骨架实现被称为AbstractInterface,这里的Interface是指所实现的接口的名字。例如,Collections Framework为每个重要的集合接口都提供了一个骨架实现,包括AbstractCollection、AbstractSet、AbstractList和AbstractMap。将他们称为SkeletalCollection、SkeletalSet、SkeletalList和SkeletalMap也是有道理的,但是现在Abstract的用法已经根深蒂固。

例如,下面是一个静态工厂方法,它包含一个完整的、功能齐全的List实现:

// Concrete implementation built atop skeletal implementation
static List<Integer> intArrayAsList(final int[] a){
  if(a == null)
    throw new NullPointerException();
  return new AbstractList<Integer>(){
    public Integer get(int i){
      return a[i];
    }
    @Override 
    public Integer set(int i, Integer val){
      int oldVal = a[i];
      a[i] = val;
      return oldVla;
    }
    public int size(){
      return a.length;
    }
  }
}

注意,这个例子中只提供一个静态工厂,并且这个类还是个不可被访问的匿名类(anonymous class),它被隐藏在静态工厂的内部。

骨架实现的美妙之处在于,他们为抽象类提供了实现上的帮助,但又不强加“抽象类被用作类型定义时”所特有的严格限制。如果预置的类无法扩展骨架实现类,这个类始终可以手工实现这个接口。此外,骨架实现类仍然能够有助于接口的实现。实现了这个接口的类可以把对于接口方法的调用,转发到一个内部私有类的实例上,这个内部私有类扩展了骨架实现类。这种方法被称作模拟多重继承(simulated multiple inheritance),这项技术具有多重继承的绝大多数优点,同时又避免了相应的缺点。

骨架类的实现的一般步骤是,找出接口中的基本方法,在抽象类中声明为抽象方法,然后用这些基本方法来实现其他方法,所谓基本方法,就是通过将这些方法组合或是变换,可以实现其他的方法。

使用抽象类来定义允许多个实现的类型,与使用接口相比有一个明显的优势:抽象类的演变比接口的演变要容易的多。可以在抽象类中增加新的方法,始终可以增加具体方法,他包含合理的默认实现。然而,该抽象类的所有实现都将提供这个新的方法。对于接口,这样是行不通的。要想在公有接口中增加方法,而不破坏实现这个接口的所有现有的类,这是不可能的。之前实现该接口的类增加同样地新方法,这样可以在一定程度上减小由此带来的破坏。所有不从骨架实现类继承的接口实现仍然会遭到破坏。

因此,设计公有的接口要非常谨慎。接口一旦被公开发行,并且已被广泛实现,再想改变这个接口几乎是不可能的。你必须在初次设计的时候就保证接口是正确的。在发行新接口的时候,最好的做法是,在接口被“冻结”之前,尽可能让更多的程序员用尽可能多的方式来实现这个新接口。这样有助于在依然可以改正缺陷的时候就发现他们。

简而言之,接口通常是定义允许多个实现的类型的最佳途径。这条规则有个例外,即当演变的容易性比灵活性和功能更为重要的时候。在这种情况下,应该使用抽象类来定义类型,但前提是必须理解并且可以接受这些局限性。如果你导出了一个重要的接口,就应该坚持考虑同时提供骨架实现类。最后,应该尽可能谨慎地设计所有的公有接口,并通过编写多个实现来对他们进行全面的测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值