背景 泛型在JDK1.5引入,其本质是一种参数化类型(Parameterized Type),在使用时传入实际类型即可 ,即可以将操作的数据类型指定为方法签名中的一种特殊参数,可以作用在类、接口、方法中。泛型是编译期的一种概念,主要是用于编译期类型安全检查(编译之后泛型会被擦除)。
常用泛型类型常量 1 2 3 4 5 E:元素(Element),多用于java集合框架 K:关键字(Key) N:数字(Number) T:类型(Type) V:值(Value) 
 
 
泛型反例 1 2 3 4 5 6 7 8 9 10 11 public  class  Test1   {	public  static  void  main (String[] args)   { 		List list = new  ArrayList(); 		list.add("abc" ); 		list.add(100 ); 		list.add(10.09 ); 		for (int  i = 0 ; i < list.size(); i++) { 			System.out.println((String)list.get(i)); 		} 	} } 
 
编译期通过,并且运行结果如下:
1 2 3 abc Exception in thread "main"  java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 	at com.sunld.Test1.main(Test1.java:14 ) 
 
如何在编译期完成校验? 
泛型接口 与泛型类的定义类似,参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  interface  Callable <V >  {    V call ()  throws  Exception ; } class  MyGeneric1 <T > implements  Callable <T > {	@Override  	public  T call ()  throws  Exception  { 		return  null ; 	} } class  MyGeneric2  implements  Callable <String > {	@Override  	public  String call ()  throws  Exception  { 		return  null ; 	} } 
 
泛型类 泛型类的声明与非泛型类的类似,在类的名称后面增加类型参数。语法如下:  
1 2 3 class  className <泛型标识 extends |super  上限|下限, ...> {  private  泛型标识  genericType; } 
 
泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。如果上限或下限有多个限制,可以使用&处理。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。简单示例:  
1 2 3 4 5 6 7 8 9 class  MyGeneric <T > {	private  T t; 	public  T getT ()   { 		return  t; 	} 	public  void  setT (T t)   { 		this .t = t; 	} } 
 
泛型方法 一种可以接受不同参数类型的方法,并且可以根据入参进行参数返回,尤其是反射处理数据转换比较常用。
注意:方法上是否定义泛型和类上是否定义没有必然的联系
 
语法参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  static  <T> T executeGenericMethod (Class<T> cls)  {    try  {         return  cls.newInstance();     } catch  (InstantiationException e) {         e.printStackTrace();     } catch  (IllegalAccessException e) {         e.printStackTrace();     }     return  null ; } 
 
类型擦除 定义 泛型是编译期的概念,在编译之后的字节码中不包含泛型的信息(为了解决该问题,java在字节码中引入Signature、LocalVariableTypeTable)。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除 。比如:List<Object>和 List<String>等类型,在编译之后都会变成 List。JVM 看到的只是 List,而由泛型附加的类型信息对JVM来说是不可见的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package  com.sunld;import  java.util.HashMap;import  java.util.List;import  java.util.Map;public  class  TestGenericType2 <T >  {	 	private  T t; 	 	private  List<String> list; 	 	private  static  int  temp1 = 10 ; 	private  final  int  temp2 =20 ; 	private  final  static  int  temp3=30 ; 	 	 	public  static  void  main (String[] args)   { 		Map<String, String> map = new  HashMap<String,String>(); 		map.put("a" , "valueaa" ); 		String value = map.get("a" ); 	} } 
 
编译后的结果:  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 警告: 二进制文件Test/bin/com.sunld.TestGenericType2包含com.sunld.TestGenericType2 Classfile /D:/Workspaces/java/TestJVM/Test/bin/com/sunld/TestGenericType2.class   Last modified 2020 -5 -14 ; size 1287  bytes   MD5 checksum 7ad53533c7723c97d0ec9bb863b415ca   Compiled from "TestGenericType2.java"  public  class  com .sunld .TestGenericType2 <T  extends  java .lang .Object > extends  java .lang .Object   minor version: 0   major version: 52    flags: ACC_PUBLIC, ACC_SUPER Constant pool:    #1 = Class              #2             // com/sunld/TestGenericType2    #2 = Utf8               com/sunld/TestGenericType2    #3 = Class              #4             // java/lang/Object    #4 = Utf8               java/lang/Object    #5 = Utf8               t    #6 = Utf8               Ljava/lang/Object;    #7 = Utf8               Signature    #8 = Utf8               TT;    #9 = Utf8               list   #10 = Utf8               Ljava/util/List;   #11 = Utf8               Ljava/util/List<Ljava/lang/String;>;   #12 = Utf8               temp1   #13 = Utf8               I   #14 = Utf8               temp2   #15 = Utf8               ConstantValue   #16 = Integer            20   #17 = Utf8               temp3   #18 = Integer            30   #19 = Utf8               <clinit>   #20 = Utf8               ()V   #21 = Utf8               Code   #22 = Fieldref           #1.#23         // com/sunld/TestGenericType2.temp1:I   #23 = NameAndType        #12:#13        // temp1:I   #24 = Utf8               LineNumberTable   #25 = Utf8               LocalVariableTable   #26 = Utf8               <init>   #27 = Methodref          #3.#28         // java/lang/Object."<init>":()V   #28 = NameAndType        #26:#20        // "<init>":()V   #29 = Fieldref           #1.#30         // com/sunld/TestGenericType2.temp2:I   #30 = NameAndType        #14:#13        // temp2:I   #31 = Utf8               this   #32 = Utf8               Lcom/sunld/TestGenericType2;   #33 = Utf8               LocalVariableTypeTable   #34 = Utf8               Lcom/sunld/TestGenericType2<TT;>;   #35 = Utf8               main   #36 = Utf8               ([Ljava/lang/String;)V   #37 = Class              #38            // java/util/HashMap   #38 = Utf8               java/util/HashMap   #39 = Methodref          #37.#28        // java/util/HashMap."<init>":()V   #40 = String             #41            // a   #41 = Utf8               a   #42 = String             #43            // valueaa   #43 = Utf8               valueaa   #44 = InterfaceMethodref #45.#47        // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   #45 = Class              #46            // java/util/Map   #46 = Utf8               java/util/Map   #47 = NameAndType        #48:#49        // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   #48 = Utf8               put   #49 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   #50 = InterfaceMethodref #45.#51        // java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;   #51 = NameAndType        #52:#53        // get:(Ljava/lang/Object;)Ljava/lang/Object;   #52 = Utf8               get   #53 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;   #54 = Class              #55            // java/lang/String   #55 = Utf8               java/lang/String   #56 = Utf8               args   #57 = Utf8               [Ljava/lang/String;   #58 = Utf8               map   #59 = Utf8               Ljava/util/Map;   #60 = Utf8               value   #61 = Utf8               Ljava/lang/String;   #62 = Utf8               Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;   #63 = Utf8               SourceFile   #64 = Utf8               TestGenericType2.java   #65 = Utf8               <T:Ljava/lang/Object;>Ljava/lang/Object; {   static  {};     descriptor: ()V     flags: ACC_STATIC     Code:       stack=1 , locals=0 , args_size=0           0 : bipush        10           2: putstatic     #22                 // Field temp1:I          5 : return        LineNumberTable:         line 13 : 0          line 15 : 5        LocalVariableTable:         Start  Length  Slot  Name   Signature   public  com.sunld.TestGenericType2();     descriptor: ()V     flags: ACC_PUBLIC     Code:       stack=2 , locals=1 , args_size=1           0 : aload_0          1: invokespecial #27                 // Method java/lang/Object."<init>":()V          4 : aload_0          5 : bipush        20           7: putfield      #29                 // Field temp2:I         10 : return        LineNumberTable:         line 7 : 0          line 14 : 4          line 7 : 10        LocalVariableTable:         Start  Length  Slot  Name   Signature             0       11      0   this    Lcom/sunld/TestGenericType2;       LocalVariableTypeTable:         Start  Length  Slot  Name   Signature             0       11      0   this    Lcom/sunld/TestGenericType2<TT;>;   public  static  void  main (java.lang.String[])  ;     descriptor: ([Ljava/lang/String;)V     flags: ACC_PUBLIC, ACC_STATIC     Code:       stack=3 , locals=3 , args_size=1           0: new           #37                 // class java/util/HashMap          3 : dup          4: invokespecial #39                 // Method java/util/HashMap."<init>":()V          7 : astore_1          8 : aload_1          9: ldc           #40                 // String a         11: ldc           #42                 // String valueaa         13: invokeinterface #44,  3           // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;         18 : pop         19 : aload_1         20: ldc           #40                 // String a         22: invokeinterface #50,  2           // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;         27: checkcast     #54                 // class java/lang/String         30 : astore_2         31 : return        LineNumberTable:         line 45 : 0          line 46 : 8          line 47 : 19          line 50 : 31        LocalVariableTable:         Start  Length  Slot  Name   Signature             0       32      0   args   [Ljava/lang/String;             8       24      1    map   Ljava/util/Map;            31        1      2  value   Ljava/lang/String;       LocalVariableTypeTable:         Start  Length  Slot  Name   Signature             8       24      1    map   Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>; } SourceFile: "TestGenericType2.java"  Signature: #65                          // <T:Ljava/lang/Object;>Ljava/lang/Object; 
 
从Signature属性的得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们在编码时能通过反射手段取得参数化类型的根本依据。
擦除过程 首先是找到用来替换类型参数的具体类。这个具体类一般是 Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。
泛型擦除的问题 
用泛型不可以区分方法签名 
泛型类的静态变量是共享 
 
泛型边界 
 extends T>表示该通配符所代表的类型是 T 类型的子类。 
 super T>表示该通配符所代表的类型是 T 类型的父类。
 
 
类型通配符? 在java中如果使用泛型,例如List<Interger>和List<Number>其实是两种类型(类型之间转换会出现转换异常 ),之间没有任务关系,如果想要接收不同类型的参数,则需要引入通配符的概念,List<?>(?表示所有泛型中的父类)泛型内是不存在父子关系,但是利用通配符可以产生类似的效果 。
具体说明 假设给定的泛型类型为G,两个具体的泛型参数X、Y,当中Y是X的子类
G<? extends Y> 是 G<? extends X>的子类型 
G 是 G<? extends X>的子类型  
G<?> 与 G<? extends Object>等同 
 
其他 优点 
适用于多种数据类型执行相同的代码(代码复用) 
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型) 
 
局限性 
不能实例化泛型类:T t = new T(); 
静态变量不能引用泛型变量:private static T t1;;非静态变量可以引用:private T t1; 
未声明泛型的方法不能引用泛型变量:public static T getT1(){return t1;};声明之后可以:public static <T> T executeGenericMethod(Class<T> cls){return null;} 
基本类型无法作为泛型类型:List<int> list = new ArrayList<>(); 
无法使用instanceof关键字或==判断泛型类的类型:list instanceof List<String> 或者list == List<String> 
泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的,即类型擦除之后的class信息相同 
泛型数组可以声明但无法实例化:Test1<Integer>[] array = new Test1<Integer>[10];,去掉泛型即可Test1<Integer>[] array = new Test1[10]; 
泛型类不能继承Exception或者Throwable 
不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出 
 
泛型继承规则 
对于泛型参数是继承关系的泛型类之间是没有继承关系的:List<Integer>与List<Number> 
泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E> 
泛型类的继承关系在使用中同样会受到泛型类型的影响 
 
泛型与反射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package  com.sunld;import  java.lang.reflect.ParameterizedType;import  java.lang.reflect.Type;public  class  TestGenericType1 <T >  {	private  T t; 	public  T getT ()   { 		return  t; 	} 	public  void  setT (T t)   { 		this .t = t; 	} 	 	public  static  void  main (String[] args)   { 		TestGenericType1<Integer> a = new  TestGenericType1<Integer>() {}; 		Type superclass = a.getClass().getGenericSuperclass(); 		System.out.println(superclass); 	     		Type type = ((ParameterizedType)superclass).getActualTypeArguments()[0 ];  	    System.out.println(type); 	} } 
 
参考 
Java 泛型在实际开发中的应用  
Java泛型详解  
Java总结篇系列:Java泛型  
java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一