做的网站太大怎么办,域名解析到别的网站,手机版商城网站都有哪 些功能,安全之要无论你是否听过java泛型的协变与逆变#xff0c;我们直接进入例子#xff0c;一起来看一下java泛型比较高级的用法。例子1#xff1a;copy函数第1个例子我们实现copy函数#xff0c;它将List中的元素复制到List中。JavaList src Arrays.asList(1,2,3,4,5);List dst new A…无论你是否听过java泛型的协变与逆变我们直接进入例子一起来看一下java泛型比较高级的用法。例子1copy函数第1个例子我们实现copy函数它将List中的元素复制到List中。JavaList src Arrays.asList(1,2,3,4,5);List dst new ArrayList();12ListsrcArrays.asList(1,2,3,4,5);ListdstnewArrayList();不用泛型copy1实现的参数类型是死的Javaprivate static void copy1(List src, List dst) {for (Integer obj : src) {dst.add(obj);}}12345privatestaticvoidcopy1(Listsrc,Listdst){for(Integerobj:src){dst.add(obj);}}然后这样调用Javacopy1(src, dst); // 死板不通用1copy1(src,dst);// 死板不通用因为Integer是Number的子类所以自然可以存储到List中。问题copy1函数只能搞定List拷贝给List无法处理其他类型。基本泛型copy2由简单实现改进引入泛型参数即可Javaprivate static void copy2(List src, List dst) {for (T1 obj : src) {dst.add((T2)obj);}}12345privatestaticvoidcopy2(Listsrc,Listdst){for(T1obj:src){dst.add((T2)obj);}}然后这样调用Javacopy2(src, dst); // 能用但没有编译期约束运行不安全(比如dst是List类型)1copy2(src,dst);// 能用但没有编译期约束运行不安全(比如dst是List类型)我们知道T1(Integer)继承自T2(Number)的子类所以加入dst时只需要强制向上转型即可通过编译。问题T1继承自T2是我们假设的如果dst是一个List那么上述代码将试图将Integer obj转型为String虽然代码通过了编译但是在运行时将失败。如果可以约束T1与T2之间的父子关系那么这个函数将更加健壮。泛型加强约束copy3在泛型基础上引入约束Javaprivate static void copy3(List extends T src, List super T dst) {for (T obj : src) {dst.add(obj);}}12345privatestaticvoidcopy3(List?extendsTsrc,List?superTdst){for(Tobj:src){dst.add(obj);}}调用Javacopy3(src, dst); // 最灵活编译期约束运行一定安全1copy3(src,dst);// 最灵活编译期约束运行一定安全我们期望的约束是src中的元素类型A必须继承自dst中的元素类型B(A与B一样也可以)那么就可以安全的将src中的元素加入到dst中(A向上转型为B)。既然A类型必须继承自B那么A与B继承层次中间一定存在某个类型T所以我们可以做出约束src里必须是T的子类List extends T上述例子中 extends T就是Integer。dst里必须是T的父类List super T上述例子中 super T就是Number。 extends T作为T的子类当然可以向上转型为 super T作为T的父类所以这个约束就是我们想要的效果。我们可以猜想编译器也一定会推断T为Number类型此时可以令上述约束成立实际上我们并不需要关心T到底是啥反正我们对T类型也做不了什么事。此时我们的copy3函数功能已经很强大可以支持将List拷贝给List这样的调用同时也会在编译器就禁止掉将List拷贝给List这样的错误写法。例子2map函数mapper是一个泛型接口Javaprivate static interface Mapper {R map(T t);}123privatestaticinterfaceMapper{Rmap(Tt);}接受T类型参数返回R类型参数。我们接下来实现map函数接受1个List与1个Mapper返回1个新List。我们固定输入是List类型返回为List类型。JavaList list Arrays.asList(1,2,3,4,5);1ListlistArrays.asList(1,2,3,4,5);不用泛型实现map1函数Javaprivate static List map1(List list, Mapper mapper) {List l new ArrayList();for (Integer t : list) {Number r mapper.map(t);l.add(r);}return l;}12345678privatestaticListmap1(Listlist,Mappermapper){ListlnewArrayList();for(Integert:list){Numberrmapper.map(t);l.add(r);}returnl;}调用Java// 写死类型List ret1 map1(list, new Mapper() {Overridepublic Number map(Integer integer) {return integer * 1.2;}});1234567// 写死类型Listret1map1(list,newMapper(){OverridepublicNumbermap(Integerinteger){returninteger*1.2;}});map1函数写死了List元素类型、Mapper输入类型、Mapper返回值类型。问题不能支持其他类型需要引入泛型。基本泛型map2将上述Integer换成T泛型参数Number换成R泛型参数Javaprivate static List map2(List list, Mapper mapper) {List l new ArrayList();for (T t : list) {R r mapper.map(t);l.add(r);}return l;}12345678privatestaticListmap2(Listlist,Mappermapper){ListlnewArrayList();for(Tt:list){Rrmapper.map(t);l.add(r);}returnl;}调用Java// 能用但mapper入参类型必须与list泛型完全一样返回值类型必须与ret2泛型完全一样List ret2 map2(list, new Mapper() {Overridepublic Number map(Integer integer) {return integer * 1.2;}});1234567// 能用但mapper入参类型必须与list泛型完全一样返回值类型必须与ret2泛型完全一样Listret2map2(list,newMapper(){OverridepublicNumbermap(Integerinteger){returninteger*1.2;}});原理很简单输入T类型的列表经过mapper处理T类型后返回R类型的列表。问题List ret2要求R类型匹配为Numberlist要求T类型匹配为Integer因此就导致Mapper也必须是Mapper使用起来看不出啥问题但灵活性是有一定局限的下面解释。泛型放宽约束当map函数遍历List中的每个Integer时如果Mapper接受Integer当然最准确但如果Mapper能够接受Number类型作为输入那么其实把Integer传给Mapper也是可以运行的因为Integer可以向上转型到Number。同样道理让Mapper返回Number类型当然最准确但如果Mapper返回是Number子类(比如Double)其实Double是可以向上转型插入到List中的这样也完全正确。所以我们应该让Mapper的泛型参数具备灵活性而不是完全等于T和R。理解了就非常容易写出map3函数Javaprivate static List map3(List list, Mapper super T,? extends R mapper) {List l new ArrayList();for (T t : list) {R r mapper.map(t);l.add(r);}return l;}12345678privatestaticListmap3(Listlist,Mapper?superT ,?extendsRmapper){ListlnewArrayList();for(Tt:list){Rrmapper.map(t);l.add(r);}returnl;}调用Java// 最灵活mapper入参类型只要是list泛型的父类即可返回值类型只要是ret3泛型的子类即可List ret3 map3(list, new Mapper() {Overridepublic Double map(Number number) {return number.doubleValue() * 1.2;}});1234567// 最灵活mapper入参类型只要是list泛型的父类即可返回值类型只要是ret3泛型的子类即可Listret3map3(list,newMapper(){OverridepublicDoublemap(Numbernumber){returnnumber.doubleValue()*1.2;}});读懂上述泛型必须学会阅读顺序根据实参和返回值T和R类型先被明确List ret3明确了返回值的R就是Number。List list明确了T就是Integer。在T和R已明确情况下我们通过? super T的方式令Mapper的入参类型放宽只要是T(Integer)的任意父类即可接受T的传入。通过? extends R的方式令Mapper的返回类型放宽只要是R(Number)的任意子类即可插入到List l中。总结你应该听说过java的协变、逆变、PECS原则等概念我认为理解上述例子应该是不需要死记硬背的1理解这3个符号之间的继承父子关系 extends T 继承自 T 继承自 super T2结合向上类型转换一切皆可分析。如果文章帮助了你请帮我点击1次谷歌广告或者微信赞助1元钱感谢知识星球有更多干货内容对我认可欢迎加入