本博文涉及的代码已上传Github,结合代码看,更容易理解:https://github.com/ZNKForSky/GenericsDemo
中文名:泛型 英文名:Generics
我问世的背景
在我还未问世之前,Java程序猿&媛很容易在使用集合过程中碰到类型转换异常(ClassCastException)。
我伴随着JDK1.5的发布降生,我为程序猿&媛提供了编译时类型检测机制,如果你们在编译时不小心犯错,我是不能容忍你们的,你们必须修复它。
我的本质
我的本质是参数化类型,就是将类型参数化,方法的定义和调用想必大家都不陌生,我与之类似,只不过定义方法时传的参数是一个具体的数据类型,而定义我的时候是一个未知的数据类型,调用方法时传的实参是一个具体的值,而调用我时传的是一个具体的数据类型。
泛型类
我存在于三个地方,分别是泛型类、泛型接口、泛型方法。首先我从泛型类开始让大家了解我。
创建泛型类,只需要在类的后面添加类似
我有一个小缺点,就是不能支持基本数据类型,这个是有历史原因的,后面再讲。
在创建泛型类对象时,如果没有指定泛型具体类型,将按Object处理:
Lottery
泛型类的几个Tips:
1>已指定父类或父接口泛型类型,子类或实现类不需要再指定泛型类型;否则,子类或实现类的泛型标识必须包含与父类或父接口同样的泛型标识。
2>原始类型是没有指定任何类型参数的泛型类或接口的名称(有点没表达清楚,我也不知道怎么讲,看下面的代码应该就清楚了<~.~>):
Box numberBox1 = new Box<>();//要创建Box的参数化类型,必须为形式类型参数T提供一个实际的类型参数
Box box = new Box();//如果省略实际的类型参数,则创建Box的原始类型
但是,非泛型类或接口类型不是原始类型。
原始类型显示在遗留代码中,因为许多 API 类(例如 Collections 类)在 JDK 5.0 之前不是泛型的。使用原始类型时,基本上可以获得 pre-generics 行为——Box 为你提供 Object。为了向后兼容,允许将参数化类型赋值给其原始类型,但将原始类型分配给参数化类型,则会出现unchecked警告,如果你使用原始类型调用在相应的泛型类中定义的带有泛型标识的方法(包括泛型方法和以泛型标识作为形参的普通方法),也会出现unchecked警告:
上图所述问题就需要用到泛型通配符解决:我们用“?”可以代替任意类型,并且使用“extends”关键字指定我的上界,这样就相当于为装Integer的Box和装Number的Box建立了继承关系。
PS:“使用super关键字可以为我指定下界”
private static void showBoxDate(Box extends Number> box) {
Number firstDate = box.getFirstDate();
System.out.println("firstDate === " + firstDate);
}
泛型接口
泛型接口与泛型类定义方式类似,不再赘述:
public interface Plate {
public void set(T t);
public T get();
}
泛型方法
泛型方法的定义是在方法的返回值类型前面添加类似
public AIPlate getAIPlate(){
return new AIPlate();
}
/**
* 随机获取奖品的泛型方法
* @param list 奖品的List
* @param 未知类型的奖品
* @return 具体的奖品
*/
public T getPrize(ArrayList list){
return list.get(random.nextInt(list.size()));
}
泛型方法的调用:
//小明给的这个盘子只能装香蕉
AIPlate aiPlate = xiaoMing.getAIPlate();
//方法泛型推断
AIPlate aiPlate1 = xiaoMing.getAIPlate();
由上图可以看出,创建泛型类对象Lottery时传入的泛型类型是Integer,但调用泛型方法时返回值类型却是String,由此可见,泛型方法的泛型标识与泛型类的泛型标识没有一毛钱关系,它的类型是由调用者独立决定的。
我的庐山真面目