您的位置:首页 > Web前端

读《Effective java 中文版》(17)

2005-01-16 12:50 176 查看

读《Effective java 中文版》(17)

第16条:接口优于抽象类

  Java语言中的接口与抽象类的一些区别:

抽象类允许包含某些方法的实现,接口不允许。

为实现一个由抽象类定义的类型,它必须成为抽象类的一个子类。任何一个类,只要它定义了所有要求的方法,并且遵守通用约定,则它就允许实现一个接口。

java只允许单继承,抽象类作为类型定义受到了极大的限制。

  看一下接口:

  已有的类可以很容易被更新,以实现新的接口。只要:增加要求的方法,在类的声明上增加implements子句。

  接口是定义mixin(混合类型)的理想选择。一个mixin是指这样的类型:一个类除了实现它的基本类型(primary type)之外,还可以实现这个mixin类型,以表明它提供了某些可选择的行为。接口之所以能定义mixin,因为它允许可选的功能可被混合到一个类型的基本类型中,而抽象类不能用于定义mixin类型,同样的理由是因为它们不能被更新到已有的类中:一个类不可能有一个以上的父类,并且在类层次结构中没有适当的地方来放置mixin。

  接口使得我们可以构造出非层次结构的类型结构。看例子:

public interface Singer{

AudioClip sing(Song s);

}

public interface Songwriter{

Song compose(boolean hit);

}

  为解决歌唱家本人也能做曲的情况,可以很简单地做到:

public interface SingerSongwriter extends Singer, Songwriter{

AudioClip strum();

void actSensitive();

}

  如果用抽象类来做,会是如何?

  接口使得安全地增强一类的功能成为可能,做法是使用第14条介绍的包装类模式。如果用抽象类型来做,则程序员除了使用继承没有别的方法。

  接口不允许包含方法的实现。把接口和抽象类的优点结合起来,对于期望导出的每一个重要接口,都提供一个抽象的骨架实现(skeletal implementaion)类。接口的作用仍是定义类型,骨架实现类负责所有与接口实现相关的工作。按照惯例,骨架实现被称为AbstractInterface(注:此interface是所实现的接口的名字,如AbstractList,AbstractSet)。看一个静态工厂:

//List adapter for int array

static List intArrayAsList(final int[] a){

if (a==null) throw new NullPointerException();

return new AbstractList(){

public Object get(int i){

return new Integer(a[i]);

}

public int size(){

return a.length;

}

public Object set(int i,Object o){

int oldVal=a[i];

a[i]=((Integer)o).intValue();

return new Integer(oldVal);

}

}

}

  这个例子是一个Adapter,它使得一个int数组可以被看作一个Integer实例列表(由于存在int和Integer之间的转换,其性能不会非常好)

  骨架实现的优美之外在于,它们为抽象类提供了实现上的帮助,但又没有强加“抽象类被用做类型定义时候”所特有的严格限制。对一地一个接口的大多数实现来讲,扩展骨架实现类是一个很显然的选择,当然它也只是一个选择而已。

  实现了这个接口的类可以把对于接口方法的调用,转发到一个内部私有类的实例上,而这个内部私有类扩展了骨架实现类。这项技术被称为模拟多重继承。

  编写一个骨架实现类相对比较简单,首先要认真研究接口,并且确实哪些方法是最为基本的(primitive),其他的方法在实现的时候将以它们为基础,这些方法将是骨架实现类中的抽象方法;然后须为接口中的其它方法提供具体的实现。骨架实现类不是为了继承的目的而设计的。(怎么理解?)看例子:

//skeletal implementation

public abstract class AbstractMapEntry implements Map.Entry{

//primitives

public abstract Object getKey();

public abstract Object getValue();

//Entries in modifiable maps must override this method

public Object setValue(Object value){

throw new UnsupportedOperationException();

}

//Implements the general contract of Map.Entry.equals

public boolean equals(Object o){

if (o==this) return true;

if (!(o instanceof Map.Entry)) return false;

Map.Entry arg=(Map.Entry)o;

return eq(getKey(),arg.getKey())&&eq(getValue(),arg.getValue());

}

private static boolean eq(Object o1,Object o2){

return (o1==null?02==null:o1.equals(o2));

}

//implements the general contract of Map.Entry.hashCode

public int hashCode(){

return (getKey()==null?0:getKey.hashCode())^(getValue()==null?0:getValue().hashCode());

}

}

  使用抽象类来定义允许多个实现的类型,比使用接口有一个明显的优势:抽象类的演化比接口的演化要容易的多。在后续的发行版中,如果希望在抽象类中增加一个方法,只增加一个默认的合理的实现即可,抽象类的所有实现都自动提供了这个新的方法。对于接口,这是行不通的。虽然可以在骨架实现类中增加一方法的实现来解决部分问题,但这不能解决不从骨架实现类继承的接口实现的问题。由此,设计公有的接口要非常谨慎,一旦一个接口被公开且被广泛实现,对它进行修改将是不可能的。

Posted by Hilton at February 16, 2004 08:57 PM | TrackBack
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: