准备材料:
当A≤B时有f(B)≤f(A)成立,称之为逆变(contravariant);
当A≤B时有f(A)≤f(B)成立,称之为协变(covariant);
当A≤B时上述两个式子均不成立,称之为不变(invariant);
关于理解协变逆变的总原则:
类型赋值时,A = B ,前提条件是要保证A >= B ;
类型变量取值时,严格按照 ? super T 代表 >= T, ? extends T 代表 <= T;
脑子记住这两条即可,不用死记什么 写用 <? super T>,读用什么 <?extends T> ;
网上见了太多的文章,花了很大篇幅讲理论,最后看完了,可能当时觉得说的也挺有道理,真正操作起来自己又各种没把握,总觉得操作性差.
废话不多说,直接上代码,实战操作;
1 class A { 2 3 } 4 5 class B extends A { 6 7 } 8 9 class C extends B { 10 11 } 12 13 14 class AC<T> { 15 private void testSuper(AC<? super T> as) { 16 System.out.println(as); 17 } 18 19 private void add(T t) { 20 System.out.println(t); 21 } 22 } 23 24 /********************************************************多组合理解泛型类参数类型与方法参数类型的关联*****************************************************/ 25 26 @Test 27 public void testS() { 28 //这里是类型变量取值;这里方法testSuper的参数类型是AC<? super T> as,而泛型类参数类型是T,具体到这类这里就是B, 29 // 所以AC<? super T> as 这里的 <? super T>具体选取类型就要求 >= B 30 AC<B> acb = new AC<>(); 31 AC<A> a = new AC<>(); 32 AC<B> b = new AC<>(); 33 AC<C> c = new AC<>(); 34 35 acb.testSuper(a); 36 acb.testSuper(b); 37 // acb.testSuper(c); //error 这里?是C 不符合 ? >= B ; 38 39 /*********************************************************/ 40 //这里属于类型赋值;这里error的情况同java.util.ArrayList的add方法操作 ,注意add方法的参数类型与泛型类上的参数类型是一致的是T,这里T = ? super B; 41 // 这里虽然声明了T >= B ,但是T不是一定>= A的(因为A > B),所以这里在保证原则一的条件下,add方法的参数类型最高只能取到B; 42 AC<? super B> ac = new AC<>(); 43 A am = new A(); 44 B bm = new B(); 45 C cm = new C(); 46 47 //ac.add(am); //error 48 ac.add(bm); 49 ac.add(cm); 50 51 AC<A> aca = new AC<>(); 52 //ac.testSuper(aca);//error 这里为啥不可以呢?看ac声明当前这里的T是 ? super B,所以testSuper方法的参数类型是 AC<? super T> 这里的T保底也就是B, 53 //AC<? super T> 这里的 并不能保证 ? super T 是 >= A的,所以error 54 55 //这里ArrayList范例 56 /*********************************************************/ 57 58 ArrayList<? super B> alb = new ArrayList<>(); 59 60 A a1 = new A(); 61 B b1 = new B(); 62 C c1 = new C(); 63 //alb.add(a1); //error 64 alb.add(b1); 65 alb.add(c1); 66 67 } 68 69 70 71 72 /*********************************************************************泛型协变逆变实践操作**********************************************************/ 73 74 //下面这些是本人参考java8 Function接口仿写的,加了自己的理解 75 76 interface IFunction<T, R> { 77 R apply(T t); 78 79 //道理类似下面的IBiFunction的分析 80 default <V> IFunction<T, V> andThen(IFunction<? super R, ? extends V> after) { 81 return (T t) -> after.apply(apply(t)); 82 } 83 } 84 85 interface IBiFunction<T, U, R> { 86 R apply(T t, U u); 87 88 //类型赋值的原则 :A = B ,前提条件是保证 A >=B 89 //假设after是 IFunction<M,N> after , 因为M是用来接收apply(t,u)返回的类型R的,即有等式M = R, 所以M可以是? super R,而且这也不是死的,你取值M =R也可,只要符合类型赋值原则; 90 //因为N是after的返回类型,最终它要返回给IBiFunction<T, U, V>这里的V的,即有V = N , 所以N可以是 ? extends V,同样这里你取N =V也可以; 91 default <V> IBiFunction<T, U, V> andThen(IFunction<? super R, ? extends V> after) { 92 return (T t, U u) -> after.apply(apply(t, u)); 93 } 94 }
读者遇到协变逆变代码自己可以尝试按上面笔者说的原则进行分析,或者有难以分析的代码也欢迎提供给笔者来分析以检验笔者总结的分析原则.