一招彻底掌握java协变逆变

准备材料:

当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