栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java通配符-详细篇--一通百通

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java通配符-详细篇--一通百通

通配符
  • ? 无界通配符
  • ? extends T 上界通配符
  • ? super T 下届通配符

通配符主要用于的变量声明及形参列表,作用于对同类型不同泛型类型的对象无法使用统一的泛型类型进行定义或接受。

从侧面来说给通配符增加解析,自然也具有了泛型类型转换的能力。上面的话有点绕,多说无益,看下面的例子就一目了然了。

通配符在普通局部方法中基本没有使用的意义,他的作用一般用于形参列表。

下面对通配符的应用场景进行举例:

//如下方法,我定义了多个ArrayList集合,这多个ArrayList都声明了泛型,什么类型都有。
public void testMethod(){
    List lm = new ArrayList<>(){{
            add(new HashMap(){{put("name","赵六");}});
        }};
    List ls = new ArrayList<>(){{
        add("张三");
    }};
    List ln = new ArrayList<>(){{
        add(1);
        add(2);
    }};
    
    //这时我需要调用一个方法对以上集合进行处理,进行遍历操作
    List l= processList(lm);
    List l= processList(ln);
    
    //上界通配符时看此方法
    processListExtends(ls);
    
    //下界通配符看此方法
    processList(ln);
}


public List processList(List list){//如果定义为List list,上面testMethod方法对该方法调用时,直接受检异常
    
    if (null!=list){
        //如下对传入的List集合进行了遍历,遍历的时候就得需要Object类型进行接收遍历
        //这时候问题来了,刚才不是说不能用Object类型的泛型来接收吗这时候怎么用上Object了
        //因为泛型只能声明为引用类型,不可声明为基本数据类型,那么里面的值也一定是引用类型,
        //只要是引用类型那么它的祖类就一定是Object类型,那么自然只能使用Object进行接收处理
        //或者不想用Object进行接收,那么可以给通配符加一个界限,详细可以看下面的上界通配符和下界通配符
        for (Object obj:list){
            System.out.println(obj);
            //既然是Object自然就可以进行强转,但并不推荐这样做。
            //下面判断了如果obj是Map类型或String就进行打印,这样看确实没问题,写法也确实没问题
            //但问题出在通用性上,我调用了一个方法,然后这个方法只要是List类型都能传入,
            //结果处理的时候却只能处理Map和String类型,这种踩屎感相信开发人员都有感受。
            //因为这写法反人类有违常识。开发人员普遍认为既然能传入就应该能处理,不能处理你让我传它干啥,是不是“司马”了,擦
            //-------虽说程序的本质是模拟世界,但是上面这行话可不是指有关事业单位或面向大众的服务部门,请不要过分解读-------
            //那么针对于这种问题,我们就可以对形参进行处理,我们给通配符加一个界限,详细可以看下面的上界通配符和下界通配符
             if (obj instanceof Map){
                 System.out.println((Map)obj);
             }else if(obj instanceof String){
                 System.out.println(obj);
             }
        }
        
        //看完上面的介绍,通配符的弊端就体现出来了,因为不知道具体类型,所以不能新增。看到这可能会说泛型只能为引用类型
        //那么传入Obejct不就行了,然而非也,你可以说String和Integer都是Object类型,但不能说Object是String或Integer类型
        //注意这里说的是类型,泛型不体现继承关系。
        //如果要新增就只能增加为null,因为String不可以赋值为Objct但可以赋值为null,Integer同理,所有引用类型都同理。
        //所有null在这里相比于Object更通用。这样就是通配符的弊端只能取不能存,要存就存null,但存null好像又没啥意义只能占个位
        list.add(null);
        
    }else {
        //list可以定义为一个新的对象,这个对象可以声明为任意的泛型类型,不知道类型可以不写默认为Object类型,
        //但不能这样声明list = new ArrayList();想想上面说的应该就理解不能声明为?,
        //通配符的作用不是用来定义对象的,而是接受对象的。试问你怎么定义一个不知道类型的对象,不知道类型可以不写默认为Object
        //但Object!=? 这俩不等价。
        list = new ArrayList();
    }
    return list;
}



public void processListExtends(List list){
    if (null!=list && !list.isEmpty()){
        
        //因为增加了界限,最大或最上就是个CharSequence类型,所以可以用CharSequence类型来进行接收,
        //当然拿Object来接收也没问题,当然也仅限于最上CharSequence和祖类Object,它的子类自然是不可以
        //String和StringBuffer都是CharSequence的子类,但不能说String就是StringBuffer,但可以说这俩都是CharSequence
        //这样增加界限的好处自然而然就显现出来了,我对数据进行处理时就能使用CharSequence中的方法来进行处理,
        //如果使用Object进行接收,那么就只能使用Object里面的方法了,这自然是不推荐使用Object进行接收的。
        for (CharSequence c:list){
            System.out.println(c);
        }
        
        //无法再添加元素,换句话说Object都不行凭啥你行
        list.add(null);
    }
}


public void processListSuper(List list){
    
    if (null!=list && !list.isEmpty()){
        //这里又回到了无界通配符的时候,只能通过Object来进行接收。这块稍微难理解一点,因为无界操作符是因为没有界限所以只能通过			//Object来接,这里已经声明界限了,为什么还是只能用Object来接?
        //其实还是可以进行强转的,例如for (Integer c:(List)list),但还是那句话不推荐,这样做写的时候可能不报错但		   //运行时可能报错,也就是存在强转的风险。例如Integer的父类是Number,但Number有很多子类,比如Long,Double等,如果我将		//一个List类型的集合转换为List然后再把它传给了当前的方法,那么就可能存在强转的风险,因为Double和		//Integer就不是一个类型,这里使用的是数值类型进行的举例可能不会报异常,但如果是自己定义的对象呢。所以是存在风险的,与其		//在运行时报异常,还不如写的时候就给我报异常呢,对吧。
        
        //明白了转换风险之后,继续分析,既然直接转换成Integer不行,转换成他的直接父类Number总成了吧,其实还是不可以的,强转风		  //险依旧存在,因为实际开发中一个类可能有多层父类,直接父类上面还有好几层的父类,那么强转的风险就移到了父类,父类也迷茫了
        //不会又是哪个孙子扮我爹来忽悠我来了吧,我也不敢吱声啊,算了我先认了吧毕竟长得像,但实际一做亲子鉴定,擦果然是孙子扮的
        
        //为了这种长得像,误当爹的情况发生,我劝你只认Object
        for (Object c:list){
            System.out.println(c);
        }
    }
    
    //下界通配符相比于无界和上界有一个优势,可以新增数据了,但也仅限于Integer类型,list.add(new Number())是不可以的
    //其实不难理解,Integer可以转换为Number类型向上转型无需强转。但如果new Number();强转为Integer类型肯定会类型转换异常的
    //虽然new Number()这样的写法有问题,实际该使用一下匿名内部类,但这里为了举例就先不纠结语法,能明白意思就好。
    list.add(112);
}


public static 
    Collector flatMapping(Function> mapper,
                                   Collector downstream) {
    BiConsumer downstreamAccumulator = downstream.accumulator();
    return new CollectorImpl<>(downstream.supplier(),
                               (r, t) -> {
                                   try (Stream result = mapper.apply(t)) {
                                       if (result != null)
                                           result.sequential().forEach(u -> downstreamAccumulator.accept(r, u));
                                   }
                               },
                               downstream.combiner(), downstream.finisher(),
                               downstream.characteristics());
}



转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号