泛型小结
来源:互联网 发布:阿里云是laas还是paas 编辑:程序博客网 时间:2024/04/30 00:16
参考书籍:《Java Generics and Collections》 authors:Maurice Naftalin,Philip Wadler
《Effective Java》 author :Joshua Bloch 译者:杨春花,俞黎敏
在没有泛型之前,从集合中读取的每一个对象都必须进行转换,如果不小心插入类型错误的对象,在运行期间对象转换的时候就会出错。有了泛型之后就可以告诉编译器集合能接受哪些类型的对象,在插入的时候进行自动转换,对象转换出错时及时报错。
每个类型都定义了parameterized type参数化类型和raw type原生类型,如List<String>,List便是原生类型,String便是参数化类型。
一些术语
Wildcards(通配符)
下面这个例子介绍了带有extends和super的wildcards的用法即通配符的两种形式:? extends E和? super E,另外只写?表示? extends Object的简写。
package com.company.collections.subtypingwildcards;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Iterator;import java.util.List;/** * wildcards通配符和subtyping子类型 * 接口Collection有如下方法 * interface Collection<E> { ...//"? extends E" ?局势wildcards通配符表示任何一种类型,这里表示E的任何一种子类型 public boolean addAll(Collection<? extends E> c); ... } 类Collections如下方法 // ? super T 表示src的元素师T的subtype,dst是T的supertype public static <T> void copy(List<? super T> dst, List<? extends T> src) { for (int i = 0; i < src.size(); i++) { dst.set(i, src.get(i)); }} * */public class SubtypingWildcards {public static void main(String[] args) {//Wildcards with extends例子如下List<Number> nums = new ArrayList<Number>();List<Integer> ints = Arrays.asList(1, 2);List<Double> dbls = Arrays.asList(2.78, 3.14);//List<Number>是Collection<? extends Number>的子类型 //List<Integer>是Collection<? extends Number>的子类型,故ints可添加,dbls类似nums.addAll(ints);nums.addAll(dbls);//List<Integer> ints = Arrays.asList(1,2);//List<? extends Number> nums = ints;//because List<Integer> is a subtype of List<? extends Number>,没错//nums.add(3.14); // compile-time error只能存放Integer,不解释//Wildcards with super例子如下List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");List<Integer> ints2 = Arrays.asList(5, 6);Collections.copy(objs, ints2);for (Iterator iterator = objs.iterator(); iterator.hasNext();) {Object object = (Object) iterator.next();System.out.println(object.toString());}}}
通配符什么时候使用,以及使用中需要注意的地方,例子如下(主要介绍的是The Get and Put Principle取出存取法则)
package com.company.collections.subtypingwildcards;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.List;/** *什么时候用wildcards通配符?用? extends E还是? super E ? * *GetPut法则:从数据结构中取出数据用extends,存入数据用super,取和存都需要的时候就不用通配符 * If an extends wildcard is present, 那就只能取数据 * if a super wildcard is present, 那就只能存数据 *很好理解,取数据时,写了一方法参数有? extends E那么E的所有子类包括自己都能调用方法取数据 * 存数据时,写了一方法参数有? super E那么存的时候不仅可以存E类型的,还可以存E的超类 * * */public class GetPutPrinciple {public static void main(String[] args) {//取数据 ? extends EList<Integer> ints = Arrays.asList(1,2,3); System.out.println(sum(ints));List<Double> doubles = Arrays.asList(2.78,3.14); System.out.println(sum(doubles));List<Number> nums = Arrays.<Number>asList(1,2,2.78,3.14); System.out.println(sum(nums));//存数据 ? super E List<Integer> ints2 = new ArrayList<Integer>(); count(ints2, 5); System.out.println(ints2.toString()); //要是不用super,下面这两个调用就是非法的了 List<Number> nums2 = new ArrayList<Number>(); count(nums2, 5); nums2.add(5.0); System.out.println(nums2.toString()); List<Object> objs = new ArrayList<Object>(); count(objs, 5); objs.add("five"); System.out.println(objs.toString());//既要取数据又要存数据,不用通配符? List<Number> nums3 = new ArrayList<Number>(); System.out.println(sumCount(nums3,5)); // List<Integer> ints4 = Arrays.asList(1,2,3);// List<? extends Number> nums4 = ints;// double dbl = sum(nums4); // ok// nums4.add(3); // compile-time error // List<Object> objs5 = Arrays.<Object>asList(1,"two");// List<? super Integer> ints5 = objs;// ints5.add(3); // ok// double dbl5 = sum(ints5); // compile-time error}public static double sum(Collection<? extends Number> nums) {double s = 0.0;for (Number num : nums) s += num.doubleValue();return s;}public static void count(Collection<? super Integer> ints, int n) { for (int i = 0; i < n; i++) ints.add(i);}public static double sumCount(Collection<Number> nums, int n) { count(nums, n); return sum(nums);}}
与Array比较
区别一:
在书《Java Generics and Collections》中有讲到:array subtyping is covariant,the subtyping relation for generics is invariant。意思是说数组子类是协变量,而泛型不是,也就是,对于数组S[] 和T[],只要S是T的子类型,那么S[]便是T[]的子类型,叫协变,而泛型List<S>和List<T>,若S是T的子类型,但List<S>不是List<T>的子类型,二者相对独立。
区别二:
数组是具体化的(reified),因此数组在运行时才会知道并且检查元素类型约束,而泛型是通过擦除(erasure),所以编译期值强化他们的类型信息,在运行的时候丢弃元素类型信息,擦除可以让泛型与没有使用泛型的代码任意互用。在运行时,每个泛型类只有一种类型. 具体地说, List<Integer>, List<String> 和 List<List<String>> 在运行时都将具有相同的类型: List。
看下面例子
Integer[] ints3 = new Integer[] {1,2,3};Number[] nums3 = ints3;nums3[2] = 3.14; // array store 运行时报错,编译没问题
List<Integer> ints1 = Arrays.asList(1,2,3);List<Number> nums1 = ints1; // compile-time errornums1.add(2, 3.14);
数组的例子很好理解,nums3实际是Integer类型,加个double类型数据当然报错,nums3是在运行时动态绑定为Integer的,故编译器没错。而泛型呢,编译期就报错,因为List<Integer>不是List<Number>的子类型,Integer是Number的子类型不管用,泛型不是协变的。
实际上,我要说的的是泛型将数组运行期类型检查提前到了编译期,好处就是编译期类型检查了,运行期就没必要检查了,更加高效,另外若有错误编译期间能即时报错。
另外,泛型的方法比数组的多,数组就是存取。但是,数组毕竟比泛型效率更高,因为数组不像泛型要装箱(boxing)。
使用数组的唯一理由是: 大量的原始数据类型, 可能可以获得性能上的提升. 不过一定要谨记: 不要优化你的程序, 除非经过严格而精确的测量证明存在性能问题. 另外, 有些情况下, 由于某些遗留系统的兼容问题, 你可能仍需要使用数组.所以,尽量使用泛型而不是数组。
擦除
前面已经解释了擦除。下面举几个例子说明一下。
以下内容引用自http://zy19982004.iteye.com/blog/1977055
使用擦除的核心动机:使得泛型化的客户端代码可以使用非泛型化的类库,非泛型化的客户端代码可以使用泛型化的类库。这个被称为“兼容迁移性”。这也从侧面反应了,前期的设计多么重要,倘若JDK1.0就将泛型纳入其中,必将是Java使用者的一大福音。
package com.jyz.study.jdk.generic;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 在泛型代码内部,无法获得任何有关泛型参数类型的信息 * @author JoyoungZhang@gmail.com * */public class ClassTypeParameters {static class Frob<T>{}static class FrobF<T extends Number>{}static class FrobPM<P,M>{}private static List list1 = new ArrayList();private static List<Integer> list2 = new ArrayList<Integer>();private static Map<Integer, Integer> map1 = new HashMap<Integer, Integer>();static Frob f1 = new Frob();static FrobF<Integer> f2 = new FrobF<Integer>();static FrobPM<Integer, Double> f3 = new FrobPM<Integer, Double>();//Calss.getTypeParameters()将返回一个TypeVariable对象数组//表示有泛型声明所声明的形式类型参数public static void main(String[] args) {System.out.println(Arrays.toString(list1.getClass().getTypeParameters()));System.out.println(Arrays.toString(list2.getClass().getTypeParameters()));System.out.println(Arrays.toString(map1.getClass().getTypeParameters()));System.out.println(list1.getClass().getSimpleName());System.out.println(list2.getClass().getSimpleName());System.out.println(Arrays.toString(f1.getClass().getTypeParameters()));System.out.println(Arrays.toString(f2.getClass().getTypeParameters()));System.out.println(Arrays.toString(f3.getClass().getTypeParameters()));}}输出结果[E][E][K, V]ArrayListArrayList[T][T][P, M]
擦除原则:
- 无限定的形式类型参数将被替换为Object。比喻List<T> T是无限定的,被替换为Object。
- 有限定的形式类型参数将被替换为第一个限定类型。比喻List<T extends Comparable & Serializable>,T被替换为Comparable,也称T被擦除到了Comparable。
- C#泛型小结
- 泛型小结
- 泛型小结
- 【学习心得】泛型小结
- 泛型使用小结
- 泛型小结
- java 泛型小结
- Java 泛型小结
- 泛型小结
- java泛型小结
- java泛型小结
- Java 泛型小结
- java泛型小结
- Java泛型小结
- 泛型小结
- Java泛型小结
- 泛型小结
- java泛型小结
- 套牌車,套牌車处罚规定,张家界、益阳、常德、岳阳、郴州套牌車
- struts2+spring+mybatis入门教程七之常见问题
- coffeescript语法--问号详解
- Kinect学习DAY3:常用API
- 黑马程序员_java语法2
- 泛型小结
- VMware虚拟机网络设置简介
- 重装Windows后恢复Ubuntu启动项
- Huffman编码实现
- jqGrid的简单介绍
- “多负载识别监控平台(上位机)”技术细节 之Unit4-Form4系统设置界面
- 先根据后缀名 然后根据文件名,排序文件名列表
- Mysql查询例子
- ZOJ-3121