Java泛型速查

Java泛型出道已经很久了(从Java 1.5开始),虽有所了解,但因其比较繁琐,始终未大面积使用。再加上现在各色动态类型语言出现,本以为不太会用到了,但却发现读别人一些底层代码时还是不可避免的用到了泛型。最近在翻查泛型资料时,在网上发现一篇以实例讲解泛型几种用法的文章,非常适合快速学习泛型知识,建议有此需求的读者阅读原文。现将其部分内容及官方站点相关资料摘录如下,以备日后查阅。

官方有关泛型的教程请参考:泛型

我们都知道,编译时的bug很容易排查,而运行时bug处理起来就麻烦得多。“泛型的使用使得更多bug在编译时即可被发现,增加了代码的稳定性”。怎么做到的呢?先看例子。

不用泛型的情况:

class Info{          
    private Object var ;          
    public void setVar(Object var){  
        this.var = var ;   
    }   
    public Object getVar(){   // 返回值的类型也由外部决定
        return this.var ;   
    }   
    public String toString(){   // 直接打印   
        return this.var.toString() ;   
    }   
}; 
/***********************************************/
public class Demo {

    public static void main(String[] args) {

        Info integerInfo = new Info();

        integerInfo.setVar("10"); // 假设:本意是要用integer的,却因一时脑子短路搞成字符串了。这种错误在编译时是绝对不可能发现的,运行时也不错在这里。

        Integer someInteger = (Integer)integerInfo.getVar();//运行时出错
        System.out.println(someInteger);
    }
}

泛型可以在编译时避免这一错误:

class Info<T>{          // 此处可以随便写标识符号,T是type的简称
    private T var ;     // var的类型由T指定,即:由外部指定     
    public void setVar(T var){   // 设置的类型由外部决定
        this.var = var ;   
    }   
    public T getVar(){   // 返回值的类型也由外部决定
        return this.var ;   
    }   
    public String toString(){   // 直接打印   
        return this.var.toString() ;   
    }   
};   
/***********************************************/
public class Demo {

    public static void main(String[] args) {

        Info<Integer> integerInfo = new Info<Integer>();

        integerInfo.setVar("10"); // 脑子短路在编译时就会发现,这里会报错

        Integer someInteger = integerInfo.getVar();//这里(Integer)强制类型转换也不需要了
        System.out.println(someInteger);
    }
}

这就是泛型最普通应用的例子。注意,文件系统中是找不到T.java或T.class的,在编译过程中,所有泛型信息将被完全删除掉,只在文件系统中留下Info.class。这就是所谓的“类型擦除(Type Erasure)”,擦除后没有类型参数的泛型类和接口称之为raw type。

类型参数命名惯例

惯例是:大写单字母。

    最常用的类型参数名有:
  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S,U,V 等等 —— 第二、第三、第四个类型

普通泛型

前面的例子就是,再举有两个类型参数的例子:

class Notepad<K,V>{     // 此处指定了两个泛型类型   
    private K key ;     // 此变量的类型由外部决定   
    private V value ;   // 此变量的类型由外部决定   
    //……
}

通配符(?)

  
    public static void fun(Info<?> temp)    // 可以接收任意的泛型对象   

受限泛型

    public static void fun(Info<? extends Number> temp) // 只能接收Number及其Numbe   
    public static void fun(Info<? super String> temp)   // 只能接收String或Object类型的泛型   

泛型无法向上转型

  
        Info<String> i1 = new Info<String>() ;  // 泛型类型为String   
        Info<Object> i2 = null ;   
        i2 = i1 ;                               //这句会出错 incompatible types   

泛型接口

  
interface Info<T>{        // 在接口上定义泛型   
    public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型   
}   
class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类   
    private T var ;             // 定义属性   
    //……
    public T getVar(){   
        return this.var ;   
    }   
};  

泛型方法

    
    public <T> T fun(T t){          // 可以接收任意类型的数据。这里<T>是限定紧随着的T的,比如可以用<T extends Number> T fun(T t)
        return t ;                  // 直接把参数返回   
    }     

通过泛型方法返回泛型类型实例

    //Info定义为 class Info<T extends Number> {……}  

    public static <T extends Number> Info<T> fun(T param){//方法中传入或返回的泛型类型由调用方法时所设置的参数类型决定   
        Info<T> temp = new Info<T>() ;      // 根据传入的数据类型实例化Info   
        temp.setVar(param) ;        // 将传递的内容设置到Info对象的var属性之中   
        return temp ;   // 返回实例化对象   
    }   

用泛型统一传入的参数类型

public static <T> void add(Info<T> i1,Info<T> i2)

泛型数组

public class GenericsDemo30{   
    public static void main(String args[]){   
        Integer i[] = fun1(1,2,3,4,5,6) ;   // 返回泛型数组   
        fun2(i) ;   
    }   
    public static <T> T[] fun1(T...arg){  // 接收可变参数   
        return arg ;            // 返回泛型数组   
    }   
    public static <T> void fun2(T param[]){   // 输出   
        System.out.print("接收泛型数组:") ;   
        for(T t:param){   
            System.out.print(t + "、") ;   
        }   
    }   
};  

泛型的嵌套

        Info<Notepad<String,Integer>> d = null ;    // 将Notepad作为Info的泛型类型   
        Notepad<String,Integer> i = null ;          // Notepad指定两个泛型类型   
        i = new Notepad<String,Integer>("汤姆",30) ;    // 实例化Notepad对象   
        d = new Info<Notepad<String,Integer>>(i) ;  // 在Info类中设置Notepad类的对象 
        System.out.println("内容一:" + d.getNotepad().getKey()) ;   
        System.out.println("内容二:" + d.getNotepad().getValue()) ;   

By songwei - Posted on 25 五月 2010