泛型与 PECS 原则

Producer Extends Consumer Super.
生产者Extends,消费者Super.java

测试

类继承结构:
Executive(经理) extends Manager(管理者) extends Employee(职工) extends Object安全

还有一个测试的泛型类.测试

static class Employee {

    }
    static class Manager extends Employee {

    }

    static class Executive extends Manager {

    }
    static class Test<T> {
        T value;

        public void setValue(T value) {
            this.value = value;
        }
        public T getValue() {
            return value;
        }
    }
复制代码

你能理解下面的代码吗

? extends T 初始化this

// Test<? extends Manager> t4= new Test<Employee>(); // 报错
// Test<? extends Manager> t5 = new Test<Object>(); // 报错
        Test<? extends Manager> t6 = new Test<Manager>();
        Test<? extends Manager> t7 = new Test<Executive>();
复制代码

? super T 初始化spa

// Test<? super Manager> t1 = new Test<Executive>(); // 报错
        Test<? super Manager> t2 = new Test<Manager>();
        Test<? super Manager> t3 = new Test<Object>();
        Test<? super Manager> t4 = new Test<Employee>();
				
复制代码

能够看出,? extend T 和 ? super T 限制了能够声明类型的容许范围.以Manager为界限,? extend T 能够声明Manager以及Manager如下, ? super T能够声明Manager以及Manager以上.设计

? extends T

描述一个这样的对象/容器对象,它/它内部的对象起码是个T,你对T的全部指望均可以知足.能够作到T能够作的全部事.
所以咱们能够get(),不管容器中是T的哪一个子类或者T自己,咱们均可以看成T来操做.
这也就是生产者Extends,它能够对外提供对象.
但你不知道T实际是什么,是哪一个子类.因此你的任何set操做,或者容器的add操做都不被java容许.不然就可能出现一个装香蕉的水果篮子里出现了苹果的状况.code

Test<? extends Manager> t7 = new Test<Executive>();
// t7.setValue(new Employee()); // 报错
// t7.setValue(new Manager()); // 报错
// t7.setValue(new Executive()); // 报错
// t7.setValue(new Object()); // 报错
复制代码

使人迷惑的 ? super T

或许你觉得,按照对称性, ? super T描述的应该是一个什么均可以放的对象/容器,只要是T的父类.包括Object.
如下语句也确实不会报错.对象

Test<? super Manager> t2 = new Test<Manager>();
        Test<? super Manager> t3 = new Test<Object>();
        Test<? super Manager> t4 = new Test<Employee>();
复制代码

可是你会失望的发现.在执行set操做/容器的add操做的时候.继承

Test<? super Manager> t4 = new Test<Employee>();
        t4.setValue(new Manager());
        t4.setValue(new Executive());
// t4.setValue(new Object()); //报错
// t4.setValue(new Employee()); //报错
复制代码

你只能set/add Manager和Manager的子类. 这是由于:
? super T 确实描述了一个T的父类对象/父类容器,但不知道是哪一个父类,因此你只能set T/T的子类. 由于,全部T的子类均可以转化为任何一个T的父类. set/add 操做是绝对安全的. 这也就是消费者 Super. 能够接受对象.get

但因为你不清楚实际是那个父类,因此你只能get到一个Object.
Object o = t4.getValue(); 这是合法的,可是没有什么意义.Object对象除了一个引用,没法进行什么操做.

容器操做

这方面的例子能够看awesomehuan.com/2016/03/05/…加深印象.

巧妙的运用

public class Collections { 
  public static <T> void copy ( List<? super T> dest, List<? extends T> src) {  
      for (int i=0; i<src.size(); i++) 
        dest.set(i,src.get(i)); 
  } 
}
复制代码

上面代码中copy方法的功能是将src中的数据复制到dest中,这里src就是生产者,dest就是消费者。 设计这样的方法,好处就是,能够复制任意类型的List,通用性特别强。

参考

stackoverflow.com/questions/2…

awesomehuan.com/2016/03/05/…

相关文章
相关标签/搜索