Java泛型那些事
Java泛型是JDK1.5之后开始引入的一个特性,提供了编译时类型安全检测机制。
也就是在编译期就可检测说类型异常。实质就是类型的参数化,将我们使用的类型转成参数。
Java泛型是伪泛型,他在编译期会进行类型擦除。
举个例子,比如ArrayList<integer> 和ArrayList<string> 在编译期,转换成字节码之后也就是.class文件,进行了类型擦除,JVM不会关心你带的类型是什么,都会变成ArrayList。</string></integer>
类型擦除
我们常说的类型擦除,其实就是将之前的<>包含的类型转成了Object类。JVM在编译的时候首先会进行一次类型检查,然后在编译的过程中,我们之前的setvalue和getValue在编译的过程中其实会内部有两个重载函数,JVM最后进行重写的时候,会重写已经强转之后的函数,因此不会产生类型转换异常,也不会在转换之后在进行一次强转。
class com.tao.test.DateInter extends com.tao.test.Pair<java.util.Date> { com.tao.test.DateInter(); Code: 0: aload_0 1: invokespecial #8 // Method com/tao/test/Pair."<init>":()V 4: return public void setValue(java.util.Date); //我们重写的setValue方法 Code: 0: aload_0 1: aload_1 2: invokespecial #16 // Method com/tao/test/Pair.setValue:(Ljava/lang/Object;)V 5: return public java.util.Date getValue(); //我们重写的getValue方法 Code: 0: aload_0 1: invokespecial #23 // Method com/tao/test/Pair.getValue:()Ljava/lang/Object; 4: checkcast #26 // class java/util/Date 7: areturn public java.lang.Object getValue(); //编译时由编译器生成的巧方法 Code: 0: aload_0 1: invokevirtual #28 // Method getValue:()Ljava/util/Date 去调用我们重写的getValue方法; 4: areturn public void setValue(java.lang.Object); //编译时由编译器生成的巧方法 Code: 0: aload_0 1: aload_1 2: checkcast #26 // class java/util/Date 5: invokevirtual #30 // Method setValue:(Ljava/util/Date; 去调用我们重写的setValue方法)V 8: return }
以上就是反编译之后的代码。
类型擦除的好处和坏处
我们常说凡事都有两面性,类型擦除的好处,就是我们不必在编码的过程中,每次都要进行强转,以及出现不必要的类型转换异常。
坏处也有,有可能会出现类型转换的多态异常。
简单的说,当我们在类型转换的时候,class P<t>,我们让Q extends P<date> ,我们让Q去继承P,然后重写P的方法,当然会将P中的T转换成Date。
但是!
P在JVM中进行了类型擦除,已经将P中的T转换成了Object类型,那么当我们继承重写的时候出现的就是一开始我们要重写,但是现在前面的类型都变了,就变成了重载,我们之前的父类方法还在,这样就会出现后期我们其实多态调用的时候,并没有重写。
解决的方法就是使用桥方法。
请看上面的代码,我们使用javap -className进行反编译代码,发现JVM在解决这个问题,使用了内部重载,子类真正覆盖的方法其实上伪父类的方法,桥方法中的类型都是Object,那么就不会出现多态异常。</date></t>
Java泛型我们一般分为泛型类,泛型接口,泛型方法。
泛型类
public class Animal<T>{ private T key; public Animal(T key){ this.key=key; } public T getKey(){ return key; } } // 当我们进行实现的时候 Animal<String> animal=new Animal<String>("cat");
泛型接口
public interface Animal<T>{ public T method(); } //实现接口不指定类型 public class Cat<T> implements Animal<T>{ public T method(){ return null; } } //实现接口,指定类型 public class Dog<T> implements Animal<String>{ public String method(){ return "WoW"; } }
泛型方法
public static<E> void printArray(E[] array){ for(E element:array){ System.out.println(element); } } //使用这个方法 Integer[] array1={1,2,3}; String[] array2={"a","b","c"}; printArray(array1); printArray(array2);
泛型的常用通配符
T表示具体的Java类型
?表示不确定的类型
E 表示element
K,V表示Java键值对Key,Value
零零碎碎的总结