准备材料:
当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 15 class AC<T> { 16 private void testSuper(AC<? super T> as) { 17 System.out.println(as); 18 } 19 20 private void testExtends(AC<? extends T> as) { 21 System.out.println(as); 22 } 23 24 private void add(T t) { 25 System.out.println(t); 26 } 27 } 28 29 30 /********************************************************多组合理解泛型类参数类型与方法参数类型的关联*****************************************************/ 31 32 @Test 33 public void testS() { 34 //这里是类型变量取值;这里方法testSuper的参数类型是AC<? super T> as,而泛型类参数类型是T,具体到这类这里就是B, 35 // 所以AC<? super T> as 这里的 <? super T>具体选取类型就要求 >= B 36 AC<B> acb = new AC<>(); 37 AC<A> a = new AC<>(); 38 AC<B> b = new AC<>(); 39 AC<C> c = new AC<>(); 40 41 acb.testSuper(a); 42 acb.testSuper(b); 43 // acb.testSuper(c); //error 这里?是C 不符合 ? >= B ; 44 45 /*********************************************************/ 46 //这里属于类型赋值;这里error的情况同java.util.ArrayList的add方法操作 ,注意add方法的参数类型与泛型类上的参数类型是一致的是T,这里T = ? super B; 47 // 这里虽然声明了T >= B ,但是T不是一定>= A的(因为A > B),所以这里在保证原则一的条件下,add方法的参数类型最高只能取到B; 48 AC<? super B> ac = new AC<>(); 49 A am = new A(); 50 B bm = new B(); 51 C cm = new C(); 52 53 //ac.add(am); //error 54 ac.add(bm); 55 ac.add(cm); 56 57 AC<A> aca = new AC<>(); 58 //ac.testSuper(aca);//error 这里为啥不可以呢?看ac声明当前这里的T是 ? super B,所以testSuper方法的参数类型是 AC<? super T> 这里的T保底也就是B, 59 //AC<? super T> 这里的 并不能保证 ? super T 是 >= A的,所以error 60 61 //这里ArrayList范例 62 /*********************************************************/ 63 64 ArrayList<? super B> alb = new ArrayList<>(); 65 66 A a1 = new A(); 67 B b1 = new B(); 68 C c1 = new C(); 69 //alb.add(a1); //error 70 alb.add(b1); 71 alb.add(c1); 72 73 74 /**************************************************************/ 75 AC<? extends B> acbb = new AC<>(); 76 A a2 = new A(); 77 B b2 = new B(); 78 C c2 = new C(); 79 //add(T t),这里T = ? extends B 即 T <= B ,泛型类类型可能实际选取是T < C的类型,所以下面这三个add都无法保证匹配类型赋值原则,所以是error; 80 // acbb.add(a2);//error 81 // acbb.add(b2);//error 82 // acbb.add(c2);//error 83 84 //这里类型变量推导, <? super T> = <? super <? extends B>> = <? super B> ;依据类型变量取值原则,要取 >=B 的类型变量; 85 acbb.testSuper(new AC<A>()); 86 acbb.testSuper(new AC<B>()); 87 // acbb.testSuper(new AC<C>());//error 88 89 //类型变量推导, <? extends T> = <? extends <? extends B>> (注意类参数类型? extends B实际取类型可以 < C),依据类型变量取值原则,以下三个皆不可; 90 // acbb.testExtends(new AC<A>());//error 91 // acbb.testExtends(new AC<B>());//error 92 // acbb.testExtends(new AC<C>());//error 93 94 95 } 96 97 98 /*********************************************************************泛型协变逆变实践操作**********************************************************/ 99 100 interface IFunction<T, R> { 101 R apply(T t); 102 103 //道理类似下面的IBiFunction的分析 104 default <V> IFunction<T, V> andThen(IFunction<? super R, ? extends V> after) { 105 return (T t) -> after.apply(apply(t)); 106 } 107 } 108 109 interface IBiFunction<T, U, R> { 110 R apply(T t, U u); 111 112 //类型赋值的原则 :A = B ,前提条件是保证 A >=B 113 //假设after是 IFunction<M,N> after , 因为M是用来接收apply(t,u)返回的类型R的,即有等式M = R, 所以M可以是? super R,而且这也不是死的,你取值M =R也可以,只要符合类型赋值原则; 114 //因为N是after的返回类型,最终它要返回给IBiFunction<T, U, V>这里的V的,即有V = N , 所以N可以是 ? extends V,同样这里你取N =V也可以; 115 default <V> IBiFunction<T, U, V> andThen(IFunction<? super R, ? extends V> after) { 116 return (T t, U u) -> after.apply(apply(t, u)); 117 } 118 }
读者遇到协变逆变代码自己可以尝试按上面笔者说的原则进行分析,或者有难以分析的代码也欢迎提供给笔者来分析以检验笔者总结的分析原则.
原文:https://www.cnblogs.com/mylittlecabin/p/11782190.html