Java泛型06 : 通配符:上边界、下边界与无界
超级通道: Java泛型学习系列-绪论
本章主要对Java泛型的通配符进行说明。
1.概念简介
在Java泛型定义时:
- 用<T>等大写字母标识泛型类型,用于表示未知类型。
- 用<T extends ClassA & InterfaceB …>等标识有界泛型类型,用于表示有边界的未知类型。
在Java泛型实例化时:
- 用<?>标识通配符,用于表示实例化时的未知类型。
- 用<? extends 父类型>标识上边界通配符,用于表示实例化时可以确定父类型的未知类型。
- 用<? super 子类型>标识下边界通配符,用于表示实例化时可以确定子类型的未知类型。
2.<T>与<?>的区别
<T>:泛型标识符,用于泛型定义(类、接口、方法等)时,可以想象成形参。
<?>:通配符,用于泛型实例化时,可以想象成实参。
<T>示例:
/*** <p>Title: <T>与<?>示例</></></p>* @author 韩超 2018/2/23 16:33*/
static class Demo1<T>{private T t;
}
<?>示例:
//<?>示例
//我们可以确定具体类型时,直接使用具体类型
Demo1<Integer> integerList = new Demo1<Integer>();
//我们不能确定具体类型时,可以使用通配符
Demo1<? extends Number> numberList = null;
//我们能够确定具体类型时,再实例化成具体类型
numberList = new Demo1<Integer>();
numberList = new Demo1<Double>();
3.通配符详解
向上转型与向下转型
为了更好的理解通配符的,我们首先需要回顾Java向上转型与向下转型的概念。
向上转型:
向上转型:子类型通过类型转换成为父类型(隐式的)。
示例如下:
//子类Integer转换成父类型Object(向上转型)
Object integer = new Integer(1);
向上转型有很多好处,如减少重复代码、使代码变得简洁,提高系统扩展性,这里就不多讲述了。
向下转型:
向下转型:父类型通过类型转换成为子类型(显式的,有风险需谨慎)。
//向下转型(有风险需谨慎)
Integer integer1 = (Integer) new Object();
integer1.intValue();
运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integerat pers.hanchao.generics.wildcard.BoundedWildcardsDemo.main(BoundedWildcardsDemo.java:126)
因为子类型存在一些父类型没有的方法,所以这种向下转型存在安全隐患。
类层次结构
为了更好的理解通配符,假设以下类层次关系:
//人员 是最高父类
Person
//工人 继承自 人员
Worker extends Person
//攻城狮 继承自 工人
Programmer extends Worker
//Java攻城狮 继承自 攻城狮
JavaProgrammer extends Programmer
//Java架构师 继承自 Java攻城狮
JavaArchitectProgrammer extends JavaProgrammer
都是很简单的类,类相关代码不再这里多展示,只展示各类的初始化:
Person person = new Person(1,"张三");
Worker worker = new Worker(1,"张三","攻城狮");
Programmer programmer = new Programmer(1,"张三","攻城狮","Java");
JavaProgrammer javaProgrammer = new JavaProgrammer(1,"张三","攻城狮","Java","架构师");
JavaArchitectProgrammer architectProgrammer = new JavaArchitectProgrammer(1,"张三","攻城狮","Java","架构师");
3.1.上边界通配符(<? extends 父类型>)
示例代码:
LOGGER.info("上边界类型通配符(<? extends >)示例");
//测试上边界类型通配符
//定义一个列表,唯一可以确定的是:此列表的元素类型的父类型是Programmer
List<? extends Programmer> programmerUpperList = null;
//do something...
//某种情形下,此列表被赋值成JavaProgrammer的列表
programmerUpperList = new ArrayList<JavaProgrammer>();
//赋值
programmerUpperList = Arrays.asList(new Programmer(1,"张三","攻城狮","Java"),new JavaProgrammer(1,"张三","攻城狮","Java","架构师"),new JavaArchitectProgrammer(1,"张三","攻城狮","Java","架构师")
);
//因为唯一可以确定此列表的元素类型的父类型是Programmer,所以可以将取得的元素值赋值给Programmer对象
programmer = programmerUpperList.get(0);
//Worker和Person是其父类,可以向上转型
worker = programmerUpperList.get(1);
person = programmerUpperList.get(2);
LOGGER.info(programmer.getClass().toString() + " : " + programmer.toString());
LOGGER.info(worker.getClass().toString() + " : " + worker.toString());
LOGGER.info(person.getClass().toString() + " : " + person.toString());
LOGGER.info("是什么类型,取出来就是什么类型");
//虽然此列表被实例化成JavaProgrammer的列表,但是不能确定其中的数据就是JavaProgrammer类型的,所以不能将取得的值赋值给JavaProgrammer对象
// javaProgrammer = programmerUpperList.get(0);
//不能确定之后实例化的类型是Programmer、JavaProgrammer还是JavaArchitectProgrammer,所以不接受add
// programmerUpperList.add(programmer);
总结:add方法受限,可以进行取值。
LOGGER.info("上边界类型通配符(<? extends>):因为可以确定最大类型,所以可以以最大类型去获取数据。但是不能写入数据。\n");
运行结果:
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:52 - 上边界类型通配符(<? extends >)示例
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:70 - class pers.hanchao.generics.wildcard.Programmer : Programmer{lang='Java'}
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:71 - class pers.hanchao.generics.wildcard.JavaProgrammer : JavaProgrammer{post='架构师'}
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:72 - class pers.hanchao.generics.wildcard.JavaArchitectProgrammer : JavaArchitectProgrammer{level='架构师'}
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:73 - 是什么类型,取出来就是什么类型
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:79 - 上边界类型通配符(<? extends>):因为可以确定最大类型,所以可以以最大类型去获取数据。但是不能写入数据。
总结:
上边界类型通配符可以确定父类型,回顾向上转型与向下的概念:
在获取数据时 [ 返回类型 get(){ return this.t;} ]:
- 因为可以确定父类型,所以可以将返回类型设置为父类型。
- 虽然不能确定返回 this.t的是何种类型,但是this.t的类型肯定是返回类型的子类型,可以通过
向上转型
成功获取。
在写入数据时 [ void set(参数类型 t){ this.t = t;} ]
- 因为可以确定父类型,所以可以将参数类型设置为父类型。
- 虽然不能确定 this.t 字段的具体类型,但是肯定是参数类型的子类型,所以此时set方法进行的是向下转型,存在很大风险。Java泛型为了减低类型转换的安全隐患,不允许这种操作。
上边界类型通配符(<? extends 父类型>):
因为可以确定父类型,所以可以以父类型去获取数据(向上转型)。但是不能写入数据。
3.2.无边界通配符(<?>)
通过前面的章节Java泛型04 : 泛型类型擦除中提到的类型擦除,我们可以很容易分析出来下面的结论:
无边界通配符<?> 等同于 上边界通配符<? extends Object>。
所以关于无边界通配符(<?>)就很好理解了。
示例代码:
LOGGER.info("无边界类型通配符示例,无边界=上边界Object");
//无边界类型通配符示例
List<?> programmerNoList = null;
//do something ...
//某种情形下,此列表被赋值成Programmer的列表
programmerNoList = new ArrayList<Programmer>();
programmerNoList = Arrays.asList(1,programmer);
//唯一可以确定其最大类是Object ,所以可以根据Object类型取值
Object obj0 = programmerNoList.get(0);
Object obj1 = programmerNoList.get(1);
LOGGER.info(obj0.getClass().toString() + " : " + obj0.toString());
LOGGER.info(obj1.getClass().toString() + " : " + obj1.toString());
LOGGER.info("无边界类型通配符(<?>):等同于(<? extends Object>),因为可以确定最大类型为Object,所以可以以Object类型去获取数据。但是不能写入数据。\n");
运行结果:
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:99 - 无边界类型通配符示例,无边界=上边界Object
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:109 - class java.lang.Integer : 1
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:110 - class pers.hanchao.generics.wildcard.Programmer : Programmer{lang='Java'}
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:111 - 无边界类型通配符(<?>):等同于(<? extends Object>),因为可以确定最大类型为Object,所以可以以Object类型去获取数据。但是不能写入数据。
总结:
无边界类型通配符(<?>):
无边界类型通配符(<?>) 等同于 上边界通配符<? extends Object>
因为可以确定父类型是Object,所以可以以Object去获取数据(向上转型)。但是不能写入数据。
3.3.下边界通配符(<? super 子类型>)
示例代码:
LOGGER.info("下边界类型通配符(<? super >)示例");
//测试下边界类型通配符
//定义一个列表,唯一可以确定的是:此列表的元素类的子类型是Programmer
List<? super Programmer> programmerLowerList = null;
//do something ...
//某种情形下,此列表被赋值成Worker的列表
programmerLowerList = new ArrayList<Worker>();
//因为无法确定对象的实例化类型是Programmer、Worker还是Person,所有不能get
// programmer = programmerLowerList.get(0);
// worker = programmerLowerList.get(0);
//因为唯一可以确定此列表的元素类型的子类是Programmer,所以可以添加Programmer类型的对象及其子类型
programmerLowerList.add(programmer);
programmerLowerList.add(javaProgrammer);
programmerLowerList.add(architectProgrammer);
LOGGER.info(programmerLowerList);
LOGGER.info("存的什么类型,就是什么类型");
LOGGER.info("下边界类型通配符(<? super>):因为可以确定最小类型,所以可以以最小类型去写入数据。但是不能获取数据。\n");
运行结果:
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:81 - 下边界类型通配符(<? super >)示例
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:95 - [Programmer{lang='Java'}, JavaProgrammer{post='架构师'}, JavaArchitectProgrammer{level='架构师'}]
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:96 - 存的什么类型,就是什么类型
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:97 - 下边界类型通配符(<? super>):因为可以确定最小类型,所以可以以最小类型去写入数据。但是不能获取数据。
下边界类型通配符可以确定子类型,回顾向上转型与向下的概念:
在获取数据时 [ 返回类型 get(){ return this.t;} ]:
- 因为可以确定子类型,所以可以将返回类型设置为子类型。
- 虽然不能确定返回的是何种类型,但是返回的对象this.t的类型肯定是返回类型的父类型,此时进行的是向下转型。Java泛型为了减低类型转换的安全隐患,不允许这种操作。
在写入数据时 [ void set(参数类型 t){ this.t = t;} ]
- 因为可以确定子类型,所以可以将参数类型设置为子类型。
- 虽然不能确定this.t字段的具体类型,但是肯定是参数类型的父类型,所以此时set方法进行的肯定是向上转型,写入成功。
联系到类型擦除,我们可以继续分析出来下面的结论:
下边界通配符<? super 子类型> 等同于 下边界通配符<? super 子类型> + 上边界通配符<? extends Object>。
所有可以进一步进行探索。
示例代码:
LOGGER.info("下边界类型通配符(<? extends >)的继续理解:上边界类型通配符真的无法取值吗?");
//上面已经因为无法确定类型,所以无法通过向上转型取值
// programmer = programmerLowerList.get(0);
//但是可以确定其最大类型必然是Object,是否可以通过Object取值??
Object object = programmerLowerList.get(0);
LOGGER.info(object.getClass().toString() + ":" + object.toString());
LOGGER.info("下边界类型通配符(<? super >) = (<? super >) + (<? extends Object>)");
LOGGER.info("意义不大");
运行结果:
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:113 - 下边界类型通配符(<? extends >)的继续理解:上边界类型通配符真的无法取值吗?
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:118 - class pers.hanchao.generics.wildcard.Programmer:Programmer{lang='Java'}
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:119 - 下边界类型通配符(<? super >) = (<? super >) + (<? extends Object>)
2018-02-23 16:45:50 INFO BoundedWildcardsDemo:120 - 意义不大
总结:
下边界类型通配符(<? super 子类型>):
因为可以确定最小类型,所以可以以最小类型去写入数据(向上转型)。。
因为可以确定最大类型是Object,所以可以以Object去获取数据(向上转型)。但是这么做意义不大。
3.4.总结
- 上边界类型通配符(<? extends 父类型>):因为可以确定父类型,所以可以以父类型去获取数据(向上转型)。但是不能写入数据。
- 下边界类型通配符(<? super 子类型>):因为可以确定最小类型,所以可以以最小类型去写入数据(向上转型)。而不能获取数据。
- 无边界类型通配符(<?>) 等同于 上边界通配符<? extends Object>,所以可以以Object类去获取数据,但意义不大。
- 下边界类型通配符(<? super 子类型>)下边界通配符<? super 子类型> + 上边界通配符<? extends Object>,所以可以以Object类去获取数据,但意义不大。
Java泛型06 : 通配符:上边界、下边界与无界相关推荐
- 一文带你看懂java 泛型,史上最全面的泛型教学啦。
认真看这篇文章,保证你们对泛型又有新的理解,如果没有的话,请顺着网线来打我呀. 概述 引用下百度百科的回答 泛型是程序设计语言的一种特性.允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那 ...
- java泛型程序设计——通配符类型+通配符的超类型限定
[0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java泛型程序设计 的 通配符类型+通配符的超类型限定 的知识: [1]通配符类型相关 1. ...
- 剑指offer(Java实现) 从上往下打印二叉树
题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 解题思路 利用队列(链表)辅助实现. 代码实现 import java.util.ArrayList; import java.uti ...
- 剑指offer编程试题Java实现--22.从上往下打印二叉树
个人博客:小景哥哥 22.从上往下打印二叉树 题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. import java.util.ArrayList; import java.util ...
- 甄别集成灶品牌,对比集成灶排名,无边界青年这样构建无界厨房
你知道什么是"无边界"吗?"无边界"是现代年轻人重要的生活态度以及消费特征.从他们喜欢的生活好物中,我们能够感受到"无边界"青年的生活态度. ...
- 聊聊Java中的并发队列中 有界队列和无界队列的区别
转载自 https://blog.csdn.net/AJ1101/article/details/81711812 本文主要总体的说一说各种并发队列 首先来一张全体照 从有界无界上分 常见的有界 ...
- Java高级开发面试,java泛型下界通配符
微服务是什么 微服务起源于2005年Peter Rodgers博士在云端运算博览会提出的微Web服务(Micro-Web-Service),根本思想类似于Unix的管道设计理念.2014年,由Mart ...
- java 泛型与通配符
1.泛型概念:所谓泛型,就是允许在定义类,接口时,通过一个标识来表示类中某个属性的类型或者是某个方法的返回值及参数类型 2.基本使用 package com.yl.pdfdemo.day08.p2;i ...
- Java泛型详解,史上最全图文详解
泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它. 毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课. 一:泛型本质 Java 泛型(gen ...
最新文章
- 格式工厂mac版_格式工厂无广告版,支持PDF文件的转换
- Linux动态加载共享库,Linux共享库的动态加载(附测试案例)
- Blend4Web —— 开源的 WebGL 框架
- Android: 生成安卓可使用的Tflite文件
- 使用CountDownLatch模拟高并发场景
- 你还在代码里做读写分离么,试试这个中间件吧!
- 7-9 根据后序和中序遍历输出先序遍历 (10 分)
- 小时候有哪些丑事,让你终身难忘?
- NAT端口映射到物理机
- 台式计算机装机软件选择,装机软件哪个好?小编教你最好的装机软件推荐
- libtorrent java_Libtorrent 之 NDK 编译
- 【公司邮箱如何申请】怎么写加密邮件,企业邮箱支持吗?
- 【架构师实践课】单体和微服务怎么选?单体到微服务怎么转?
- java题兔子第三个月生_JAVA编程之古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子...
- oracle数据库向表中新增字段以及插入一行数据
- 一个简单的微分对策问题求解及其Matlab实现
- # CF #807 Div.2(A - D)
- Reactor构架模式
- Linux 远程工具
- 线程池自定义拒绝策略