《Effective java》笔记(第二版) --第四章(17-19)

个人笔记

第四章 ——类和接口


第17条: 要么为继承而设计,并提供文档说明,要么就禁止继承

例子:

public abstract class AbstractCollection<E>
中有add和addAll方法
add

       public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

addAll

    public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))//注意!这里调用了add
                modified = true;
        return modified;
    }

可以看到,AbstractCollection中的实现中addAll中调用了add函数,这种纯粹属于实现细节,不会被导出到API之中,所以,当这个类的用户,继承了AbstractCollection实现了add,那么就会导致其他的Api被污染破坏,出现不可预料的结果,那么,这种情况的补救方法,就是这个类的作者,必须给出明确的说明来作为文档注释导出,用this implements …开头,表明这个实现的特异性,而addAll的文档正是这样做的

/**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the specified collection, and adds
     * each object returned by the iterator to this collection, in turn.
     *
     * <p>Note that this implementation will throw an
     * <tt>UnsupportedOperationException</tt> unless <tt>add</tt> is
     * overridden (assuming the specified collection is non-empty).
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     * @throws IllegalArgumentException      {@inheritDoc}
     * @throws IllegalStateException         {@inheritDoc}
     *
     * @see #add(Object)
     */

不只是这种覆写的方式可能会造成破坏,还有一点,每个子类的构造函数可能会调用super.()或者多个参数来初始化父类的域,那么这个父类的构造中,也就是你设计的类中,就不该对于那些可以被@Override,覆写的方法进行调用,否则也会造成破坏,
如果你做不到这样,或者没有理由让这个类被继承的话,那么请用final修饰去禁止继承它

第18条: 接口优于抽象类

其他的高级语言,比如c++,那用java继承的时候会感觉有些奇怪,在c++世界中是允许多继承的,并且有一个叫做virtual的继承关系,Effective c++中对这个关键字进行了解释

virtual函数的本质是由虚指针和虚表来控制,虚指针指向虚表中的某个函数入口地址,就实现了多态

类的作者将会付出额外的代价,但是好处是强制了类的子类必须这样做,少走弯路,那么java的抽象类(abstract class)也是这样的行为,但是为什么要尽量接口替代抽象类呢,java是单一继承,只能有一个extends 但是可以多个implements 接口,继承代表这层次关系,父子,而接口则代表这个实现类有怎样的行为, 总的有一下几点

现有的类可以很容易被更新,以实现新的接口

层次和平面,单一继承

接口是定义mixin(混合类型) 的理想选择

接口代表着实现了这样的功能和行为,那么很多很多的类型(功能)和基本的类型(功能)组合在一起,就不再需要多个类层次

接口允许我们构造非层次结构的类型框架

对于一个不断修改的类,最好保证原有的行为,那么势必不能随意修改类的实现,最好的方法是在原有的基础上实现(implements)各种接口来安全的扩展,而不是每多一个功能,就得实现子类去扩展,既不优雅,也破坏了类的层次结构

翻看api文档的时候,在util包中发现这样的树形继承结构

类层次图




标记了一些Abstract开头的类,发现这些常用的容器类,ArrayList,Vector,HashSet等,都有一个这样开头的父类,下面看详细的类声明(ArrayList为例子)

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

然后是ArrayList的父类AbstractList

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

集合结构图
图片来源blog
底层的容器只需要和Abstract的骨架类打交道,实现一些接口,而抽象的骨架类对一些固定的行为进行实现,为继承树最上层的容器服务
让我想起了,桥接模式,不过桥接是传递实体来服务,这个只是单纯的继承,原文是

通过对你导出的每个重要接口都提供一个抽象的骨架实现类,吧接口和抽象类的优点结合起来,接口的作用仍然是定义类型,但是骨架实现类接管了所有与接口实现相关的工作


骨架实现的美妙之处在于它们为抽象类提供了实现上的帮助,但又不强加"抽象类被用于类型定义时"所持有的严格限制
骨架实现就是为了继承而实现的


第19条: 接口只用于定义类型

java 1.5特性,static导入,使用

import static com.xxx.xxx;

就能使用其中的常量,这样就能避免把一些flag丢入了实现类中,不要使用常量接口,要么成员,要么静态导入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值