1. Q:为什么要编写泛型接口
    A:兼容性,特别是容器,不被特定对象所耦合。接口设计时,想要兼容更多对象类型,就应该想到泛型。
//v0.1: 非泛型容器。
public interface Holder {
    Object get();
    void add(Object e);
    Holder getHalf();
    void addAll(Holder h);
}
  • 设计这样的接口兼容性很强,接收返回都是Object类型,但是返回值都需调用者进行强转,不友好。
//v1.0 泛型接口
public interface Holder<E> {

    E get();

    void add(E t);

    Holder<E> getAll();

    void addAll(Holder<E> h);
}
  • 泛型接口设计中要特别注意方法的参数,当方法参数是泛型类型时,会涉及到兼容性问题。例如addAll(Holder< E> h) 方法只能接受Holder< E> 类型对象,而Holder<E的子类>类型对象是无法接受的。
// v1.1 泛型接口
public interface Holder<E> {

    E get();

    void add(E t);

    Holder<E> getAll();

    void addAll(Holder<? extends E> h);
}
  • 泛型类型中,XX<? extends E> 要大于 XX<e>,更具有兼容性。</e>

jdk参考:Collection.addAll( Collection<? extends E> c)

Q:为什么方法参数的类型参数修改为<? extends E>更具兼容性?
A:首先,java 继承实现中子类覆盖方法时,参数不能改变。 为了方法能接受更多的实际类型参数,方法的形式类型参数的类型应该设计的大一些。

Q: 为什么返回值的类型参数是 List<t>,而不是<? extends E></t>
A: java 继承实现中,子类覆盖方法的返回值类型可以缩小!。一般情况下不应该把返回值设计的较大,比如Object,非具体类型不方便调用者使用的。

  • 泛型接口使用的两种方式。应该从调用者的角度考虑采用何种实现方式:
  1. 一种是实现类直接指定接口的泛型类型参数,调用者涉及泛型,直接使用该接口。
public class NumberHolder implements Holder<Number> {
   // ......
}
  1. 另一种是只实现接口,泛型类型参数由接口调用者决定(常见于容器)。
public class BaseHolder<E> implements Holder<E> {
    // ......
}

图片说明