- 浏览: 466404 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
ty1972873004:
sunwang810812 写道我运行了这个例子,怎么结果是这 ...
Java并发编程: 使用Semaphore限制资源并发访问的线程数 -
lgh1992314:
simpleDean 写道请问,Logger.setLevel ...
Java内置Logger详解 -
sunwang810812:
我运行了这个例子,怎么结果是这样的:2号车泊车6号车泊车5号车 ...
Java并发编程: 使用Semaphore限制资源并发访问的线程数 -
jp260715007:
nanjiwubing123 写道参考你的用法,用如下方式实现 ...
面试题--三个线程循环打印ABC10次的几种解决方法 -
cb_0312:
SurnameDictionary文章我没看完,现在懂了
中文排序
Java泛型支持通配符,可以单独使用 '?' 来表示任意类型,也可以使用extends关键字表示某一个类或接口的子类,也可以使用super关键字表示某一个类,接口的父类型。
本文对读写操作的分析得出什么时候该使用extends 和 super。
<? super T>使“读”受到限制
先来看一个例子:
比如要实例化一个List<? super Integer>的numberList ,我们可以使用Integer, Number和Object来完成,如:
List<? super Integer> numberList = new ArrayList<Integer>(); List<? super Integer> numberList = new ArrayList<Number>(); List<? super Integer> numberList = new ArrayList<Object>();
从这个例子中可以看出,numberList可能是指向List<Integer>, 可能指向List<Number>, 也可能指向List<Object>, 这样多可能性将会限制“读”操作。
因为,
- 我们并不能保证读到的是Integer,因为numberList可能指向List<Number>或者List<Object>。
- 我们并不能保证读到的是Number,因为numberList可能指向List<Object>。
- 唯一能保证的的就是我们将得到一个Object或者是Object的子类的一个实例,但是我们并不知道具体的子类是什么。
如下,有如下这样的一个get方法,使用了List<? super T>:
private static <T> T get(List<? super T> list, int index) { }
那么,我们怎么知道list中存放的内容是什么类型呢? 我们只能知道是T或者T的父类,仅此而已。但T的父类具体是什么,不得而知了。
“读”操作不能使用<? super T>, 而应该使用<? extends T>, 让我们来看看Collections中的一些方法吧。
/** * Gets the ith element from the given list by repositioning the specified * list listIterator. */ private static <T> T get(ListIterator<? extends T> i, int index) { T obj = null; int pos = i.nextIndex(); if (pos <= index) { do { obj = i.next(); } while (pos++ < index); } else { do { obj = i.previous(); } while (--pos > index); } return obj; }
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) { if (c==null) return binarySearch((List) list, key); if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key, c); else return Collections.iteratorBinarySearch(list, key, c); }
<? extedns T>使“写”受到限制
一个List<? extends Number>的numberList可能指向List<Number>, 可能指向List<Integer>, 也可能指向List<Object>.
List<? extends Number> numberList = new ArrayList<Number>(); List<? extends Number> numberList = new ArrayList<Integer>(); List<? extends Number> numberList = new ArrayList<Double>();
这种多可能性将让<? extends T> 的“写”操作受到限制。
因为,
- 我们不能添加一个Integer类型的值,因为numberList可能指向List<Double>
- 我们不能添加一个Double类型的值,因为numberList可能指向的是List<Integer>
- 我们不能添加一个Number类型的值,因为numberList可能指向的是List<Integer>
我们不能添加任何对象到List<? extends T>, 那是因为我们并不能保证实际指向的是什么类型的List,所以也就不能保证想要添加的对象是List所允许的类型。
唯一能保证的是只能读取并得到一个T或者是T的子类。
上面的分析,我们可以得出一个结论, 那就是<? extends T> 不适合“写”操作,<? super T> 不适合“读”操作。
其实,
Collections中的copy方法很好的使用<? extends T> 和 <? super T>的经典案例。
另外还有一个PECS原则供参考:
PECS原则-->
在 Collections#copy方法中,src (the producing list)使用extends, 而 desc (the consuming list) 使用super. 代码如下:
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()); } } }
通配符<?>的使用
List<?>表示的是任意类型。因为编译器不知道List中容纳的是什么类型的元素,所以不能对其进行增加,修改的操作。 但是,List<?>拥有删除的功能,因为这些功能与泛型类型没有关系。
所以,List<?>适合用于与泛型类型无关的方法,比如remove, shuffle等。
我们来看看Collections中的几个方法吧:
public static void shuffle(List<?> list, Random rnd) { int size = list.size(); if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { for (int i=size; i>1; i--) swap(list, i-1, rnd.nextInt(i)); } else { Object arr[] = list.toArray(); // Shuffle array for (int i=size; i>1; i--) swap(arr, i-1, rnd.nextInt(i)); // Dump array back into list ListIterator it = list.listIterator(); for (int i=0; i<arr.length; i++) { it.next(); it.set(arr[i]); } } }
public static void swap(List<?> list, int i, int j) { final List l = list; l.set(i, l.set(j, l.get(i))); }
public static void rotate(List<?> list, int distance) { if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) rotate1(list, distance); else rotate2(list, distance); }
总结:
- 只用于“读”功能时,泛型结构使用<? extends T>
- 只用于“写”功能时,泛型结构使用<? super T>
- 如果既用于“写”,又用于“读”操作,那么直接使用<T>.
- 如果操作与泛型类型无关,那么使用<?>
发表评论
-
工厂类中移除if/else语句
2016-07-10 19:52 903面向对象语言的一个强大的特性是多态,它可以用来在代码中移除 ... -
Java编程练手100题
2014-12-11 17:13 6730本文给出100道Java编程练手的程序。 列表如下: 面 ... -
数组复制的三种方法
2014-11-30 12:57 2214本文将给出三种实现数组复制的方法 (以复制整数数组为例)。 ... -
数组复制的三种方法
2014-11-30 12:54 0本文将给出三种实现数组复制的方法 (以复制整数数组为例)。 ... -
四种复制文件的方法
2014-11-29 13:21 1743尽管Java提供了一个类ava.io.File用于文件的操 ... -
判断一个字符串中的字符是否都只出现一次
2014-11-25 12:58 2725本篇博文将给大家带来几个判断一个字符串中的字符是否都只出现一 ... -
使用正则表达式判断一个数是否为素数
2014-11-23 13:35 2169正则表达式能够用于判断一个数是否为素数,这个以前完全没有想过 ... -
几个可以用英文单词表达的正则表达式
2014-11-21 13:12 3751本文,我们将来看一下几个可以用英文单词表达的正则表达式。这些 ... -
(广度优先搜索)打印所有可能的括号组合
2014-11-20 11:58 1955问题:给定一个正整n,作为括号的对数,输出所有括号可能 ... -
随机产生由特殊字符,大小写字母以及数字组成的字符串,且每种字符都至少出现一次
2014-11-19 14:48 3980题目:随机产生字符串,字符串中的字符只能由特殊字符 (! ... -
找出1到n缺失的一个数
2014-11-18 12:57 3178题目:Problem description: You h ... -
EnumSet的几个例子
2014-11-14 16:24 8751EnumSet 是一个与枚举类型一起使用的专用 Set 实现 ... -
给定两个有序数组和一个指定的sum值,从两个数组中各找一个数使得这两个数的和与指定的sum值相差最小
2014-11-12 11:24 3329题目:给定两个有序数组和一个指定的sum值,从两个数组 ... -
Java面试编程题练手
2014-11-04 22:49 6701面试编程 写一个程序,去除有序数组中的重复数字 编 ... -
Collections用法整理
2014-10-22 20:55 9847Collections (java.util.Collect ... -
The Code Sample 代码实例 个人博客开通
2014-09-04 18:48 1418个人博客小站开通 http://thecodesample. ... -
Collections.emptyXXX方法
2014-06-08 13:37 2145从JDK 1.5开始, Collections集合工具类中预先 ... -
这代码怎么就打印出"hello world"了呢?
2014-06-08 00:37 7398for (long l = 4946144450195624L ... -
最短时间过桥
2014-04-21 22:03 4145本文用代码实现最短时间过桥,并且打印如下两个例子的最小过桥时间 ... -
将数组分割成差值最小的子集
2014-04-20 22:34 2903本文使用位掩码实现一个功能 ==》将数组分割成差值最小的子集 ...
相关推荐
在Java泛型编程中,extends和super关键字用于限制类型参数的范围。PECS原则是一个重要的原则,用于指导泛型编程。了解extends和super关键字的区别,并遵循PECS原则,可以帮助你编写更好的泛型代码。
Java51.4.1 使用Object表示泛型1.4.2 基本类型的包装1.4.3 使用接口类型表示泛型1.4.4 数组类型的兼容性1.5 利用Java5泛性实现泛型特性成分1.5.1 简单的泛型类和接口1.5.2 自动装箱/拆箱1.5.3 带有限制的通配符...
1.9 本章小结 22 本章练习 22 第2章 理解面向对象 23 2.1 面向对象 24 2.1.1 结构化程序设计简介 24 2.1.2 程序的三种基本结构 25 2.1.3 面向对象程序设计简介 27 2.1.4 面向对象的基本特征 28 2.2 UML...
Java51.4.1 使用Object表示泛型1.4.2 基本类型的包装1.4.3 使用接口类型表示泛型1.4.4 数组类型的兼容性1.5 利用Java5泛性实现泛型特性成分1.5.1 简单的泛型类和接口1.5.2 自动装箱/拆箱1.5.3 带有限制的通配符...
Java51.4.1 使用Object表示泛型1.4.2 基本类型的包装1.4.3 使用接口类型表示泛型1.4.4 数组类型的兼容性1.5 利用Java5泛性实现泛型特性成分1.5.1 简单的泛型类和接口1.5.2 自动装箱/拆箱1.5.3 带有限制的通配符...
1.5.3 带有限制的通配符 1.5.4 泛型static方法 1.5.5 类型限界 1.5.6 类型擦除 1.5.7 对于泛型的限制 1.6 函数对象 小结 练习 参考文献第2章 算法分析 2.1 数学基础 2.2 模型 2.3 要分析的...
1.5.3 带有限制的通配符 1.5.4 泛型static方法 1.5.5 类型限界 1.5.6 类型擦除 1.5.7 对于泛型的限制 1.6 函数对象 小结 练习 参考文献第2章 算法分析 2.1 数学基础 2.2 模型 2.3 要分析的...
1.4 小结 第2章 Java面向对象编程 2.1 面向对象的一些概念 2.1.1 面向对象涉及的概念 2.1.2 类和对象 2.2 面向对象的一些特性 2.2.1 继承特性 2.2.2 多态特性 2.2.3 封装特性 2.3 Java中实现的面向对象特性 2.3.1 ...
1.5本章小结18 第2章Java语言基础19 2.1Java语言的特点19 2.2Java程序的构成21 2.3数据类 型23 2.3.1基本数据类型23 2.3.2常量25 2.3.3变量26 2.3.4整型数据27 .2.3.5浮点型数据29 2.3.6字符型数据30 ...
第1篇 Java编程基础 第1章 Java开发环境的搭建(教学视频:9分钟) 2 1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境 5 1.2.4 测试JDK配置...
第1篇 Java编程基础 第1章 Java开发环境的搭建(教学视频:9分钟) 2 1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境 5 1.2.4 测试JDK配置是否成功 7...
实例258 通配符使用示例 515 实例259 泛型方法使用示例 516 实例260 泛型接口示例 518 实例261 泛型实现坐标打印 519 14.2 泛型类的继承 521 实例262 继承泛型类示例 521 实例263 继承非泛型类示例 522 实例264 泛型...
实例258 通配符使用示例 515 实例259 泛型方法使用示例 516 实例260 泛型接口示例 518 实例261 泛型实现坐标打印 519 14.2 泛型类的继承 521 实例262 继承泛型类示例 521 实例263 继承非泛型类示例 522 实例264 泛型...
Java范例开发大全(全书源程序),目录如下: 第1篇 Java编程基础 第1章 Java开发环境的搭建(教学视频:9分钟) 2 1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境...
### 小结 在准备Java及J2EE相关的面试时,需要对上述知识点有全面的掌握。了解Java基础和高级特性能够展示你对语言本身的熟练程度;掌握J2EE核心组件和安全机制则能够体现你在企业级应用开发和安全性方面的专业能力...
1.5 小结 11 第2章 基本数据类型——构建Java 大厦的基础 12 2.1 源代码注释 12 2.1.1 单行注释 12 2.1.2 区域注释 12 2.1.3 文档注释 13 2.2 基本数据类型 14 2.2.1 整型 15 2.2.2 浮点型 17 ...