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()) ;

最新评论
9 周 6 小时之前
9 周 1 天之前
11 周 5 天之前
11 周 5 天之前
17 周 2 天之前
17 周 4 天之前
19 周 3 天之前
20 周 1 天之前
20 周 2 天之前
20 周 5 天之前