一、体验泛型

  JDK1.5之前的集合类中存在的问题——可以往集合中加入任意类型的对象,例如下面代码:

 1 package cn.gacl.generic.summary;
2
3 import java.util.ArrayList;
4
5 public class GenericTest {
6
7 public static void main(String[] args) {
8 /**
9 * 不使用泛型之前ArrayList容器可以存储任意类型的对象
10 */
11 ArrayList collection1 = new ArrayList();
12 collection1.add(1);//存储Integer对象
13 collection1.add(1L);//存储Long对象
14 collection1.add("xdp");//存储String对象
15 /**
16 * 这里会报异常: JAVA.LANG.CLASSCASTEXCEPTION:
17 * JAVA.LANG.LONG CANNOT BE CAST TO JAVA.LANG.INTEGER
18 *
19 */
20 int i = (Integer) collection1.get(1);
21 }
22 }

  JDK1.5之后的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型之外的数据,例如下面的代码:

        /**
* 使用泛型限定ArrayList容器只能存储字符串类型的对象
*/
ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("孤傲苍狼");
//collection2.add(1);//报错,因为限制了collection2只能存储String类的对象,不能加入Integer类型的对象
//collection2.add(1L);//报错,因为限制了collection2只能存储String类的对象,不能加入Long类型的对象
//由于已经指明集合中存储的是字符串类型的对象,因此这里不用再强制转型了
String element = collection2.get(0);

  泛型是提供给Javac编译器看的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带参数类型说明的集合时会去去除掉“类型”信息,使程序运行不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样由于编译生成的字节码会去掉泛型的类型信息,因此只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。

  例如下面的代码就演示了"使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象"

1 ArrayList<Integer> collection3 = new ArrayList<Integer>();
2 //对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样
3 System.out.println(collection3.getClass());//结果为:java.util.ArrayList
4 System.out.println(collection3.getClass() == collection2.getClass());//结果为true
5 //使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象
6 collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
7 System.out.println(collection3.get(0));//输出的结果为:abc,这证明字符串对象确实是存储到了原本只能存储Integer对象的集合中

备注:

  1. 泛型是JDK1.5的所有新特性中最难深入掌握的部分,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中,使用泛型集合,可以将一个集合中的元素限定为一个特定类型,这样集合中就只能存储同一类型的对象,这样更安全;并且当从集合中获取一个对象时,编译器也知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
  2. 在JDK1.5之后,你还可以按原来的方式将各种不同类型的数据放到同一个集合中,但是编译时会报一个unChecked警告
  3. 泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collectin<Object>是两个没有转换关系的参数化的类型

二、了解泛型

  • ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:

    • 整个称为ArrayList<E>泛型类型
    • ArrayList<E>中的E称为类型变量或类型参数
    • 整个ArrayList<Integer>称为参数化类型
    • ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
    • ArrayList<Integer>中的<>是“typeof”
    • ArrayList称为原始类型
  • 参数化类型与原始类型的兼容性:
    • 参数化类型可以引用一个原始类型的对象,编译时编译器会报警告,例如:Collection<String> c = new Vector();
    • 原始类型可以引用一个参数化类型的对象,编译时编译器会报警告,例如:Collection c = new Vector<String>();
  • 参数化类型不考虑类型参数的继承关系:

    • Vector<String> v = new Vector<Object>();//错误,语法上不通过
    • Vector<Object> v = new Vector<String>();//错误,语法上不通过

  假设Vector<String> v = new Vector<Object>;可以的话,那么以后从v中取出的对象当作String用,而v实际指向的集合中可以加入任意类型的对象,

  假设Vector< Object > v = new Vector< String >;可以的话,那么以后可以向v中加入任意类型的对象,而v实际指向的集合中只能装String类型的对象

思考:下面的代码会报错吗?(不会报错

  • Vector v1 = new Vector<String>();//参数化类型的对象可以给原始类型的引用
  • Vector<Object> v=v1;//参数化类型的引用可以指向原始类型的对象

三、泛型中的?通配符

问题:定义一个方法,该方法可以打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?

错误的定义:

 1     /**
2 * Collection<Object>中的Object只是说明Collection<Object>实例对象中的方法接收的参数是Object
3 * Collection<Object>是一种具体的类型,new HashSet<Date>也是一种具体的类型,两者没有兼容性问题
4 * @param collection
5 */
6 public static void printCollection(Collection<Object> collection){
7 for(Object obj:collection){
8 System.out.println(obj);
9 }
10 collection.add("abc");//没错
11 collection=new HashSet<Date>();//会报告错误
12 }

正确的定义:

 1     /**这里的Collection<?>中的?表示可以传人任意的类型参数
2 * Collection<?> cols可以匹配任意参数化的类型,但是到底匹配的是什么类型,只有以后才知道
3 * 所以 cols=new ArrayList<Integer>和cols = new ArrayList<String>都可以
4 * 但是cols.add("abc")或cols.add(new Date())都不行
5 */
6 public static void printCollection(Collection<?> collection){
7 for(Object obj:collection){
8 System.out.println(obj);
9 }
10 //collection.add("abc");//报错,因为collection不知道未来匹配的一定是String类型
11 collection.size();//不报错,此方法与参数类型没有关系
12 collection=new HashSet<Date>();//这是可以的
13 }

  总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数无关的方法,不能调用与参数有关的方法

四、泛型中的?通配符的扩展

1.限定通配符?的上边界

  • 正确的写法:Vector<? extends Number> x = new Vector<Integer>();

   这里指的是?所代表的参数化类型必须是继承Number类的,如这里的?所代表的Integer类型就是继承Number类的

  • 错误的写法:Vector<? extends Number> x = new Vector<String>();

2.限定通配符?的下边界

  • 正确的写法:Vector<? super Integer> y = new Vector<Number>();

  这里指的是?所代表的参数化类型必须是Integer类的父类,如这里的?所代表的Number类型就是Integer类的父类

  • 错误的写法:Vector<? super Integer> y = new Vector<Byte>();

五、泛型的综合应用

 1 package cn.itcast.day2;
2 import java.util.HashMap;
3 import java.util.HashSet;
4 import java.util.Map;
5 import java.util.Set;
6 /**
7 * 此类是用来演示泛型的应用的
8 *
9 * @author 孤傲苍狼
10 *
11 */
12 public class GenericCase {
13 public static void main(String[] args) {
14 HashMap<String, Integer> maps = new HashMap<String, Integer>();
15 maps.put("lhm", 35);
16 maps.put("flx", 33);
17 /**
18 * 变量的命名技巧:如果以后不知道一个变量该如何命名,就可以以方法名的形式来命名,
19 * 如果要定义变量接收返回值,如果此时不知道如何定义变量名时,就直接定义成returnValue
20 */
21 Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();// 这里的变量名直接以方法名的形式定义
22 // 使用增强的for循环迭代Map容器中的key和value
23 //这里的Entry是Map类的一个内部类,map类中存储的key和value都是封装在这个Entry内部类中的
24 //这个Entry内部类提供了getKey方法取出键,getValue方法取出值
25 for (Map.Entry<String, Integer> entry : entrySet) {
26 System.out.println(entry.getKey() + ":" + entry.getValue());
27 }
28 }
29 }

在JSP页面中也经常要使用迭代标签<c:forEach>对Set或Map集合进行迭代:

1 <c:forEach items=”${map}” var=”entry”>
2 ${entry.key}:${entry.value}
3 </c:forEach>

六、自定义泛型方法

 1 package cn.itcast.day2;
2 import java.io.Serializable;
3 /**
4 * 此类是用来演示如何定义和使用泛型方法的
5 *
6 * @author 孤傲苍狼
7 *
8 */
9 public class GenericMethod {
10 public static void main(String[] args) {
11 add(3, 5);
12 Number x1 = add(3.5, 5);// Integer类型和Double类型的交集就是Number类,Number类是这两个类的父类,所以可以定义Number类型的变量来接收返回值
13 Object x2 = add(3, "abc");// Integer类型和String类型的交集就是Object类,因为Object类是所有类的父类,所以可以定义Object类型的变量来接收返回值
14 /**
15 * swap(new String[]{"abc","123","xdp"},1,2);的执行结果如下:
16 * abc 123 xdp
17 * abc xdp 123
18 * 从结果来看,索引为1的元素和索引为2的元素的确是交换了位置
19 */
20 swap(new String[] { "abc", "123", "xdp" }, 1, 2);// 调用自定义泛型方法,传入的实际参数必须是引用类型的数组
21 // swap(new int[]{1,2,3,4,5},1,3);//只有引用类型才能作为泛型方法的实际参数,这里的int[]数组是属于基本类型,不能作为泛型方法的参数,所以这样会报错
22 printArray(new Integer[]{1,2,3});//可以传入Integer类型的数组,因为Integer类型的数组是属于引用类型的
23 //printArray(new int[]{10,2,5});不能传入非引用类型的数组作为泛型方法的实际参数
24 }
25 /**
26 * 泛型方法的定义语法: 这里定义的就是一个泛型方法 方法的返回值类型是T,即任意的类型 返回值的具体类型由传入的类型参数来定
27 *
28 * @param <T>
29 * 代表任意的类型
30 * @param x
31 * @param y
32 * @return
33 */
34 private static <T> T add(T x, T y) {
35 return null;
36 }
37 /**
38 * 定义一个泛型方法来交换数组中指定索引位置的两个元素 这里传入的数组可以是任意类型的数组
39 * 传入泛型方法的实际参数类型必须是引用类型的数组,不能是基本类型的数组
40 *
41 * @param <T>
42 * @param a
43 * @param i
44 * @param j
45 */
46 private static <T> void swap(T[] a, int i, int j) {
47 // 数组中元素位置未交换前的打印结果
48 printArray(a);
49 T temp = a[i];
50 a[i] = a[j];
51 a[j] = temp;
52 System.out.println();
53 // 数组中元素位置交换后的打印结果
54 printArray(a);
55 }
56 /**
57 * 定义打印任意引用数组类型的方法
58 *
59 * @param <T>
60 * @param array
61 */
62 private static <T> void printArray(T[] array) {
63 for (T t : array) {
64 System.out.print(t + "\t");
65 }
66 }
67 /**
68 * 定义有extends限定符,并且具有多个上边界的泛型方法,各个边界使用&符号分隔
69 * @param <T>
70 */
71 public <T extends Serializable & Cloneable> void method(){}
72 }

  普通方法,构造方法和静态方法都可以使用泛型

七、泛型方法练习题

  1. 编写一个泛型方法,自动将Object类型对象转换为其他类型
  2. 定义一个泛型方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象
  3. 采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
  4. 定义一个泛型方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中
  5. 定义一个泛型方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中去
 1 /**
2 * 1.编写一个泛型方法,自动将Object类型对象转换为其他类型
3 * @param <T>
4 * @param obj
5 * @return
6 */
7 private static <T> T autoConvert(Object obj){
8 return (T)obj;
9 }
10 /**
11 * 2.定义一个泛型方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象
12 * @param <T>
13 * @param array
14 * @param obj
15 */
16 private static <T> void fillArray(T[] array,T obj){
17 for(int i=0;i<array.length;i++){
18 array[i]=obj;
19 }
20 printArray(array);
21 }
22 /**
23 * 3.采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容
24 * @param <T>
25 * @param collection
26 */
27 private static <T> void printCollection(Collection<T> collection){
28 System.out.println(collection.size());
29 for(Object obj:collection){
30 System.out.println(obj);
31 }
32 }
33 /**
34 * 4.定义一个泛型方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中
35 * @param <T>
36 * @param srcCollection
37 * @param descArray
38 */
39 private static <T> void CollectionCopyToarray(Collection<T> srcCollection,T[] descArray){
40 Iterator<T> it = srcCollection.iterator();
41 int recordElementPostion=0;
42 while(it.hasNext()){
43 descArray[recordElementPostion]=it.next();
44 recordElementPostion++;
45 }
46 printArray(descArray);
47 }
48 /**
49 * 5.定义一个泛型方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中去
50 * @param <T>
51 * @param srcArray
52 * @param descArray
53 */
54 private static <T> void srcArrayToDescArray(T[] srcArray,T[] descArray){
55 for(int i=0;i<srcArray.length;i++){
56 descArray[i]=srcArray[i];
57 }
58 printArray(descArray);
59 }
60 private static <T> void printArray(T[] array) {
61 for (T t : array) {
62 System.out.print(t + "\t");
63 }
64 }

八、自定义泛型类

  如果类的实例对象中有多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时就要采用泛型类型的方式定义,也就是类级别的泛型,语法格式如下:

 1 package cn.itcast.day2;
2 import java.util.Set;
3 import cn.itcast.day1.ReflectField;
4 /**
5 * DAO:Data Access Object(数据访问对象)
6 * 数据访问:CRUD,即增删改查
7 * @author 孤傲苍狼
8 * 此类是用来演示如何定义泛型类
9 * 此泛型类中的<E>中的E代表实际操作的类型
10 * 指明了操作类型E之后,GenericDAO类中定义的CRUD方法就都是针对于指定的类型
11 */
12 public class GenericDAO<E> {
13 private E field1; //定义泛型类型的成员变量
14 public <E> void add(E x){
15 }
16 public <E> E findById(int id){
17 return null;
18 }
19 public void delete(E obj){
20 }
21 public void delete(int id){
22 }
23 public void update(E obj){
24 }
25 //public static void update(E obj){}这样定义会报错,静态方法不允许使用泛型参数
26 public static<E> void update2(E obj){}//这样定义就可以,此时的这个静态方法所针对的类型和GenericDAO<E>中指定的类型是两个不同的类型
27 public Set<E> findByConditions(String where){
28 return null;
29 }
30 public static void main(String[] args) {
31 GenericDAO<ReflectField> dao = new GenericDAO<ReflectField>();//这里指定泛型类的操作类型是ReflectField
32 dao.add(new ReflectField(1,3));
33 ReflectField rf = dao.findById(1);
34 GenericDAO<String> dao1=null;
35 new GenericDAO<String>();
36 }
37 }

  类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下的两种方式都可以:

    GenericDAO<String> dao=null;

    new GenericDAO<String>();

注意:

  1.在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型

  2.当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。

九、通过反射获得泛型的实际类型参数

 1 package cn.itcast.day2;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.ParameterizedType;
4 import java.lang.reflect.Type;
5 import java.util.Date;
6 import java.util.Vector;
7 /**
8 * 此类是用来演示如何通过反射获得泛型的实际类型参数
9 * Hibernate中的源代码就有这样的写法
10 * @author 孤傲苍狼
11 *
12 */
13 public class UseReflectGetGenericParameter {
14 public static void main(String[] args) throws Exception {
15 /**
16 * 通过这种方式得到的字节码中是没有办法得到泛型类的实际类型参数的,
17 * 因为在编译这个泛型类时就已经把这个泛型类的实际参数给去掉了
18 * Vector<Date> v = new Vector<Date>();
19 * v.getClass();
20 */
21 Method applyMethod = UseReflectGetGenericParameter.class.getMethod(
22 "applyVector", Vector.class);
23 //得到泛型类型的参数化类型数组,Type类是Class类的父类
24 Type[] types = applyMethod.getGenericParameterTypes();
25 /**
26 * ParameterizedType这个类是一个参数化类型类,types数组中存储的都是参数化类型的参数,
27 * 这里取出第一个数组元素,并强制转换成ParameterizedType类型
28 */
29 ParameterizedType pType = (ParameterizedType) types[0];
30 System.out.println(pType.getRawType()/*得到原始类型,输出的结果为:class java.util.Vector*/);
31 System.out.println(pType.getActualTypeArguments()[0]/*获得泛型的实际类型参数,输出的结果为:class java.util.Date*/);
32 }
33 /**
34 * 利用反射可以得到这个方法的参数列表的类型
35 * 通过这个变量v是没有办法知道定义它的那个类型的
36 * 但是当把这个变量交给一个方法作为参数或者返回值去使用,
37 * Method类中提供了一系列方法可以获得方法的参数列表
38 * 并且是以泛型的那种形式来获得参数列表
39 * @param v
40 */
41 public static void applyVector(Vector<Date> v) {
42 }
43 }

java基础—泛型的更多相关文章

  1. 一天一个Java基础——泛型

    这学期的新课——设计模式,由我仰慕已久的老师传授,可惜思维过快,第一节就被老师挑中上去敲代码,自此在心里烙下了阴影,都是Java基础欠下的债 这学期的新课——算法设计与分析,虽老师不爱与同学互动式的讲 ...

  2. Java 基础 -- 泛型、集合、IO、反射

    package com.java.map.test; import java.util.ArrayList; import java.util.Collection; import java.util ...

  3. java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  4. Java基础 - 泛型详解

    2022-03-24 09:55:06 @GhostFace 泛型 什么是泛型? 来自博客 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了&quo ...

  5. java基础-泛型3

    浏览以下内容前,请点击并阅读 声明 8 类型擦除 为实现泛型,java编译器进行如下操作进行类型擦除: 如果类型参数有限制则替换为限制的类型,如果没有则替换为Object类,变成普通的类,接口和方法. ...

  6. java基础 泛型

    泛型的存在,是为了使用不确定的类型. 为什么有泛型? 1. 为了提高安全 2. 提高代码的重用率 (自动 装箱,拆箱功能) 一切好处看代码: package test1; import java.la ...

  7. java基础-泛型2

    浏览以下内容前,请点击并阅读 声明 6 类型推测 java编译器能够检查所有的方法调用和对应的声明来决定类型的实参,即类型推测,类型的推测算法推测满足所有参数的最具体类型,如下例所示: //泛型方法的 ...

  8. java基础-泛型1

    浏览以下内容前,请点击并阅读 声明 泛型的使用能使类型名称作为类或者接口定义中的参数,就像一般的参数一样,使得定义的类型通用性更强. 泛型的优势: 编译具有严格的类型检查 java编译器对于泛型代码的 ...

  9. Java基础---泛型、集合框架工具类:collections和Arrays

    第一讲     泛型(Generic) 一.概述 1.JDK1.5版本以后出现的新特性.用于解决安全问题,是一个类型安全机制. 2.JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类 ...

  10. Java基础——泛型

    一.定义 泛型(generic)是指参数化类型的能力.可以定义带泛型类型的类或方法,随后编译器会用具体的类型来替换它(泛型实例化).使用泛型的主要优点是能够在编译时,而不是在运行时检测出错误.它是jd ...

随机推荐

  1. 最棒的Unity Github 项目收集(2016)

    http://1darray.com/blog/2016/03/08/best-unity-github-repositories/ List of best public GitHub reposi ...

  2. JQ下拉加载更多

    <!DOCTYPE=html> <html> <head> <script src="jquery-1.4.2.min.js" type= ...

  3. Noip2016day1 天天爱跑步running

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...

  4. echarts3.0 实例容器不实时更新页面的问题

    var instanceId = document.getElementById(option.echartId).getAttribute('_echarts3_instance_'); if (i ...

  5. React入门看这篇就够了

    摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...

  6. 77%的Linux运维都不懂的内核问题

    前言 之前在实习时,听了 OOM 的分享之后,就对 Linux 内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后, ...

  7. webpack4.0介绍与使用(一)

    1:webpack的基本使用: ##在网页中会引用那些静态资源: js, css, images, 字体文件和模板文件(.vue)等 ##网页总引用静态资源多了以后会有那些问题: 网页加载速度慢,因为 ...

  8. eclipse 通过svn导入maven工程

    http://blog.csdn.net/zdnlp/article/details/7238194

  9. 图像分类丨Inception家族进化史「GoogleNet、Inception、Xception」

    引言 Google提出的Inception系列是分类任务中的代表性工作,不同于VGG简单地堆叠卷积层,Inception重视网络的拓扑结构.本文关注Inception系列方法的演变,并加入了Xcept ...

  10. [USACO07JAN]平衡的阵容Balanced Lineup

    [USACO07JAN]平衡的阵容Balanced Lineup 题目描述 For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) a ...