第十八条 接口优于抽象类

类可以很快的被更新,以实现新的接口,扩展功能。
例如前期编写了一个类A,然后要加入了一个新接口B,如果想让之前的类实现这个接口,添加一个implement B即可,然后在A中实现具体细节功能,完美兼容之前的版本,并不会改动A里面现有的逻辑。 接口是定义mixin(混合类型)的理想选择,和适合。同时类时实行单继承的,而接口是可以多继承,这个比较有优势,扩展性更强。 接口允许我们构造非层次结构的类型框架。一个接口代表singer,另外一个接口代表songwriter,两者之间可能并不是父子关系,有些歌唱家本身也是作曲家。我们如果用接口来定义这些类型,对于一个类,它同时实现Singer和songwriter,但,我们可以定义一个新接口,让它同时继承singer和songwriter,并且还可以添加一些新的方法。

    public interface Singer {
        AudioClip sing(Song s);
    }

    public interface Songwriter {
        Song compose(Boolean hit);
    }

    public interface SingerSongwriter extends Singer, Songwriter {
        AudioClip strum();

        void actSensitive();
    }

这个例子中,如果没有 SingerSongwriter 接口, 我们单纯的用类去实现  Singer 或  Songwriter  接口,不但不方便后期扩展,并且可能要写些臃肿的类,支持各种方法,如果系统有n个属性方法,则产生的数量最大为2的n次方,这个维护起来就麻烦了。抽象类一般感觉比较重量级,把一些公共的方法,相同的业务,进行抽取到基类,或者分层,这样形成了从上到下的体系,感觉是一个主体,可能会有动一发而动全身,感觉像是一个动物的主体;接口感觉比较轻量级,一般是为了统一规划一些公共方法,或定义一些回调,感觉属于轻量级,类似动物的四肢。我们对动物做一些简单修整时,比如添加个爪子、角之类的,改动比较小,用接口;如果要把狮子的肚子编程大象的肚子,这种重量级的改变,就要从抽象类中的架构改变了。(纯粹举例,不好的话也不要嫌弃)抽象类和接口各有优点,那能结合一下吗? 能,答案就是骨架。 最典型的例子还是 ADT 数据结构,java 中 Collection 模块,经典中的经典。还拿上一章的例子来举例

Collection

     boolean remove(Object o);

AbstractCollection
    
    public boolean remove(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext()) {
                if (it.next()==null) {
                    it.remove();
                    return true;
                }
            }
        } else {
            while (it.hasNext()) {
                if (o.equals(it.next())) {
                    it.remove();
                    return true;
                }
            }
        }
        return false;
    }

AbstractList

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

ArrayList

    public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }


从上到下,各层分层,实现了接口,并且为了继承而设计,可以原封不动的使用,也可以视情况将它子类化。我们可以随意扩展,如果单独把ArrayList源码从上到下,从子类到基类看几遍,就会发现整体设计特别好,扩展也好。就拿 remove 举例, 在 Collection 中规划处一个集合的基本功能,其中包含 添加 删除 是否包含 元素个数 等方法, AbstractCollection 中是根据迭代器模式,来实现一些功能,这种代码实现是抽象的,因为把具体逻辑交割给了迭代器,它中的迭代器还是抽象的,需要继续往下看, AbstractList 这一层表明是列表,因为不确定底层数据结构,所以只能把迭代器的增删逻辑写清楚,但具体到细节的增 删,则是抽象,因为细节到了这一层还不太好确定,  ArrayList ,好了,确定数据结构是数组,然后 remove()的具体操作就实现了; 如果去观察LinkedList,发现也一样,唯一比ArrayList不太相同的是数据结构是双链表结构,并接中间多了一层 AbstractSequentialList,因为是链表,删除的话要找到前面的和后面的元素。 如果觉得一个remove()方法不太够的话,可以再看看 各层的 contains(Object o)  方法,也是一直依赖于迭代器模式,此次,如果仔细观察 AbstractList 类,其中必须要子类重写的方法,都是添加和删除的基本的方法,其他方法例如 contains() indexOf()  clear() ,都通过用用迭代器写好了,我们如果要写个自己的 MArrayList ,我们继承 AbstractList 类,然后也用数组来填装数据,我们基本只要把 add rrmove 几个需要重写的子类给完成就可以了,其他的框架已经帮忙把功能给实现了。 至于 系统源码 ArrayList 的 contains() indexOf()  clear() 这些方法重写了,是为了提高效率,针对不同的具体的数据结构,能更有针对性的完成,性能优化, 迭代器是通用的,只有具体到自己的子类中,针对具体的数据才能写到最优,而 AbstractList 这一层,是不知道具体细节的,只能大体实现通用的方法,但性能不一定是最优。

Collection 模块是个神作,在下也是读了n多遍才读懂。 effective java 的好多章节的知识都被用到了,这个模块读懂了,本章节就完全明白了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值