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代码]不同的场景使用不同的泛型通配符的更多相关文章

  1. [改善Java代码]避开基本类型数组转换列表陷阱

    开发中经常用到Arrays和Collections这两个工具类. 在数组和列表之间进行切换.非常方便.但是也会遇到一些问题. 看代码: import java.util.Arrays; import ...

  2. [改善Java代码]警惕泛型是不能协变和逆变的

    什么叫做协变(covariance)和逆变(contravariance)? 在变成语言的类型框架中,协变和逆变是指宽类型和窄类型在某种情况下(如参数,泛型,返回值)替换或交换的特性,简单的说,协变是 ...

  3. [改善Java代码]易变业务使用脚本语言编写

    建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们 ...

  4. [改善Java代码]在明确的场景下,为集合指定初始容量

    我们经常使用ArrayList,Vector,Hashmap等集合,一般都是直接用new跟上类名声明出一个集合来,然后使用add,remove,等方法进行操作,而且因为它们是自动管理长度的,所以不用我 ...

  5. [改善Java代码]推荐在复杂字符串操作中使用正则表达式

    一.分析  字符串的操作,诸如追加.合并.替换.倒序.分隔等,都是在编码过程中经常用到的,而且Java也提供了append.replace.reverse.split等方法来完成这些操作,它们使用起来 ...

  6. [改善Java代码]非稳定排序推荐使用List

    我们知道Set与List的最大区别就是Set中的元素不可以重复(这个重复指的equals方法的返回值相等),其他方面则没有太大的区别了,在Set的实现类中有一个比较常用的类需要了解一下:TreeSet ...

  7. [改善Java代码]不同的列表选择不同的遍历方法

    一.场景: 我们来看一个场景,统计一个省的各科高考科目考试的平均分. 当然使用数据库中的一个SQL语句就能求出平均值,不过这个不再我们的考虑之列,这里只考虑使用纯Java的方式来解决.(由于我的机器配 ...

  8. [改善Java代码]枚举和注解结合使用威力更大

    注解的写法和接口很类似,都采用了关键字interface,而且都不能有实现代码,常量定义默认都是pulbic static final类型的. 他们的主要不同点是:注解在interface前加上@字符 ...

  9. [改善Java代码]优先选择线程池

    在Java1.5之前,实现多线程编程比较麻烦,需要自己启动线程,并关注同步资源,防止线程死锁等问题,在1.5版本之后引入了并行计算框架,大大简化了多线程开发. 我们知道线程有5个状态:新建状态(New ...

随机推荐

  1. 问题-PopupMenu是哪个控件调用弹出的?

    相关资料: http://bbs.csdn.net/topics/310195683 问题现象:今天有朋友问我个简单的问题,在多个Edit上弹出菜单,怎么判断是哪个Edit调用的.我想了想这个我还真不 ...

  2. jstat用法

    jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具.它可以显示本地或者远程虚拟机进程中的类装载.内存.垃圾收集.JIT编译等运行数据 ...

  3. WEB开发框架

  4. #pragma comment使用

    编程经常碰到,理解的总不是很透彻,在这里查阅资料总结一下! 在编写程序的时候,我们常用到#pragma指令来设定编译器的状态或者是指示编译器完成一些特定的动作. #pragma once : 这是一个 ...

  5. iOS中多控制器的使用

    通常情况下,一个app由多个控制器组成,当app中有多个控制器的时候,我们就需要对这些控制器进行管理. 在开发过程中,当有多个View时,可以用一个大的view去管理多个小的view,控制器也是如此, ...

  6. ALT(预警)

    1. Alert简介 Alert是一种Oracle系统中的一种机制,它可以监视系统数据库,在规定的情况下给规定用户一个通知,通知可以是邮件或者其他形式,在标注的系统和客户化系统中都是可以定义使用的 2 ...

  7. IOS6 字体高亮显示

    ios6之前在一个字符串中如果也让某个字体高亮或者特殊显示(如: 关注[ ]),需要用单独一个的标签进行显示,或者利用CoreText进行字体绘绘制,非常麻烦: 现在IOS6 中TextView,la ...

  8. php error file_get_contents()

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  9. Chrome Apps将可以打包成iOS或Android应用

    Chrome Apps 将可以在 iOS 和 Android 设备上独立运行了.开发者只要使用 Google今天 提供的工具集(toolchain)将自己的 Web App 打包,并将生成的应用上传到 ...

  10. Codeforces Round #172 (Div. 2) B. Nearest Fraction 二分

    B. Nearest Fraction Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/281/p ...