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

Java开发零碎知识点 文章被收录于专栏

零零碎碎的总结

全部评论

相关推荐

不愿透露姓名的神秘牛友
09-30 19:49
起名星人:蛮离谱的,直接要求转投销售
投递汇川技术等公司10个岗位
点赞 评论 收藏
分享
我即大橘:耐泡王
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务