[改善Java代码]不同的场景使用不同的泛型通配符
Java泛型支持通配符(Wildcard),可以单独使用一个"?"表示任意类,也可以使用extends关键字标识某一类(接口)的子类型,还可以使用super关键字标识某一类(接口)的父类型,但问题是什么时候该用extends,什么时候该用super?
(1)泛型结构只参与"读"操作则限定上界(extends 关键字)
于都如下代码,看业务逻辑操作是否还能继续?
- import java.util.Arrays;
- import java.util.List;
- public class Client {
- public static void main(String[] args) {
- read(Arrays.asList("A"));
- }
- public static <E> void read(List<? super E> list){
- for(Object obj:list){
- //业务逻辑操作
- }
- }
- }
从List列表中读取元素操作(比如一个数字列表中的求和计算),你觉得方法read能继续写下去吗?
答案是不能,我们不知道list到底存放的是什么元素,只能推断出是E类型的父类,当然也可以是E类型,单问题是E类型的父类是什么呢?
无法再推断,只有运行时才知道,那么编码期就完全无法操作了,当然,你可以把它当作是Object类型来处理,需要时再转换成E类型,但是这样完全违背了泛型的初衷.
在这种情况下,"读"操作如果期望从List集合中读取数据就需要使用extends关键字了,也就是要界定泛型的上界,代码如下:
- import java.util.Arrays;
- import java.util.List;
- public class Client {
- public static void main(String[] args) {
- read(Arrays.asList("A"));
- }
- public static <E> void read(List<? extends E> list){
- for(E e:list){
- System.out.println(e.getClass());
- //业务逻辑处理
- }
- }
- }
此时已经推断出List集合中取出的是E类型的元素,具体是什么类型的元素就要等到运行时才能确定了,但是它一定是一个确定的类型,比如read(Arrays.asList("A"))调用该方法时,可以推断出List中的元素类型是String,之后就可以对List中的元素进行操作了,如加入到另外的List<E>集合中,或者作为Map<E,V>的键等.
(2)泛型结构只参与"写"操作则限定下界(使用super关键字)
先看如下代码是否可以正常编译:
- import java.util.List;
- public class Client {
- public static void main(String[] args) {
- }
- public static void write(List<? extends Number> list){
- list.add(null);
- list.add(123);
- //The method add(int, capture#2-of ? extends Number)
- //in the type List<capture#2-of ? extends Number> is not applicable for the arguments (int)
- }
- }
编译失败,失败的原因是list中的元素类型不确定,也就是编译期无法推断出泛型类型到底是什么,是Integer类型?是Double?还是Byte?这些都符合extends关键字的定义,由于无法确定实际的泛型类型,所以编译器聚聚了此类操作.
在此种情况下只有一个元素是可以add进去的:null值,这是因为null是一个万用类型,它可以是所有类的实例对象,所以可以加入到任何列表中.
Object是否也可以?不可以,因为它不是Number的子类,而且即使把list变量修改为List<? extends Object>类型也不能加入,就是因为编译器无法推断出泛型类型,加什么元素都是无效的.
在这种"写"操作的情况下,使用super关键字限定泛型类型的下界才可以.
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
- public class Client {
- public static void main(String[] args) {
- }
- public static void write(List<? super Number> list) {
- list.add(123);
- list.add(3.14);
- }
- }
不用管是Integer类型还是Float类型,都可以加入到list列表中,因为它们都是Number累心g,zhejiu保证了泛型类的可靠性.
对于要限定上界还是下界,JDK的Collecctions.copy方法是一个非常好的例子,它实现了把源列表中的所有元素拷贝到目标列表中对应的索引位置上.代码如下:
- public static <T> void copy(List<? super T> dest, List<? extends T> src) {
- int srcSize = src.size();
- if (srcSize > dest.size())
- throw new IndexOutOfBoundsException("Source does not fit in dest");
- if (srcSize < COPY_THRESHOLD ||
- (src instanceof RandomAccess && dest instanceof RandomAccess)) {
- for (int i=0; i<srcSize; i++)
- dest.set(i, src.get(i));
- } else {
- ListIterator<? super T> di=dest.listIterator();
- ListIterator<? extends T> si=src.listIterator();
- for (int i=0; i<srcSize; i++) {
- di.next();
- di.set(si.next());
- }
- }
- }
源列表是用来提供数据的,所以src变量需要限定上界,带有extends关键字,目标列表是用来写入数据的,所以dest变量需要界定上界,带有super关键字.
如果一个泛型结构即用作"读"操作又用作"写"操作,那该如何进行限定呢?
不限定,使用确定的泛型类型即可,如List<E>.
[改善Java代码]不同的场景使用不同的泛型通配符的更多相关文章
- [改善Java代码]避开基本类型数组转换列表陷阱
开发中经常用到Arrays和Collections这两个工具类. 在数组和列表之间进行切换.非常方便.但是也会遇到一些问题. 看代码: import java.util.Arrays; import ...
- [改善Java代码]警惕泛型是不能协变和逆变的
什么叫做协变(covariance)和逆变(contravariance)? 在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是 ...
- [改善Java代码]易变业务使用脚本语言编写
建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们 ...
- [改善Java代码]在明确的场景下,为集合指定初始容量
我们经常使用ArrayList,Vector,Hashmap等集合,一般都是直接用new跟上类名声明出一个集合来,然后使用add,remove,等方法进行操作,而且因为它们是自动管理长度的,所以不用我 ...
- [改善Java代码]推荐在复杂字符串操作中使用正则表达式
一.分析 字符串的操作,诸如追加.合并.替换.倒序.分隔等,都是在编码过程中经常用到的,而且Java也提供了append.replace.reverse.split等方法来完成这些操作,它们使用起来 ...
- [改善Java代码]非稳定排序推荐使用List
我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet ...
- [改善Java代码]不同的列表选择不同的遍历方法
一.场景: 我们来看一个场景,统计一个省的各科高考科目考试的平均分. 当然使用数据库中的一个SQL语句就能求出平均值,不过这个不再我们的考虑之列,这里只考虑使用纯Java的方式来解决.(由于我的机器配 ...
- [改善Java代码]枚举和注解结合使用威力更大
注解的写法和接口很类似,都采用了关键字interface,而且都不能有实现代码,常量定义默认都是pulbic static final类型的. 他们的主要不同点是:注解在interface前加上@字符 ...
- [改善Java代码]优先选择线程池
在Java1.5之前,实现多线程编程比较麻烦,需要自己启动线程,并关注同步资源,防止线程死锁等问题,在1.5版本之后引入了并行计算框架,大大简化了多线程开发. 我们知道线程有5个状态:新建状态(New ...
随机推荐
- notepad++汉字突然横过来了
修改notepad++,汉字突然横过来了,如图, 百度了一下,原来是因为选择的字体"@微软雅黑"前面的@符号惹的祸,改成"微软雅黑"就没事了.
- chrome 在home下生成 libpeerconnection.log
chrome 在home下生成 libpeerconnection.log,比较烦恼. google了下,可以有方法绕过去,如下. /opt/google/chrome/google-chrome 找 ...
- URAL 2045 Richness of words (回文子串,贪心)
Richness of words 题目链接: http://acm.hust.edu.cn/vjudge/contest/126823#problem/J Description For each ...
- SaltStack安装(CentOS7.x)
安装基础: 参考文档:https://docs.saltstack.com/en/latest/topics/installation/rhel.html 1.导入SaltStack仓库key: wg ...
- threading模块
threading — Higher-level threading interface¶ Source code: Lib/threading.py This module constructs h ...
- aspose.cell制作excel常见写法
//设置Excel的基本格式信息 Workbook workbook = new Workbook(); Worksheet worksheet = workbook.Worksheets[]; St ...
- SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)
题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...
- JMS开发(一):基础理论认知
JMS全称是Java Message Service.其是JavaEE技术规范中的一个重要组成部分,是一种企业消息处理的规范.它的作用就像一个智能交换机,它负责路由分布式应用中各个组件所发出的消息. ...
- mysql的interval函数用法
Name: 'INTERVAL' Description: Syntax: INTERVAL(N,N1,N2,N3,...) Returns 0 if N < N1, 1 if N < N ...
- java实现简单的素数判断
素数的这个问题由来已久,大学刚接触语言的时候遇到过找素数的问题,找工作笔试的时候也遇到过素数的问题,今天就特地写这篇博文,缅怀一下. 一.什么是素数? 除了1和它本身以外不再有其他的除数整除. 二.判 ...