一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义类型。如果要编写可用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
泛型入门
Java的集合有个缺点:当我们把一个对象丢进集合里后,集合就会忘记者个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型。这是因为设计集合的程序员不知道我们要用集合来保存什么类型的对象,所以他们把集合设计成能保存任意类型的对象。这样做带来两个问题:
- 集合对元素类型没有任何限制,这样可能会引发一系列问题:例如,我们想创建一个只可以保存 Dog 对象的集合,但程序员也可以轻易地将 Cat 对象丢进去,这样会引发异常。
- 将对象丢进集合时,集合丢失了对象的状态信息,集合只知道它盛装的是 Object ,因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换会增加程序的复杂度,而且可能引发 ClassCastException。
深入泛型
所谓泛型,就是允许在定义类、接口时指定类型形参,这个类型形参将在声明变量、创建对象时确定。
定义泛型接口、类
不仅 Java 的集合都定义成了泛型,用户自己也可以定义任意泛型的类、接口,只要在定义他们时用<>来指定类型参数即可。
- 定义泛型类
package com.chao.chapterFourteenGenerics;
public class OverClass<T> {
private T over;
public T getOver(){
return over;
}
public void setOver(T over){
this.over = over;
}
public static void main(String[] args) {
//实例化一个boolean类型的对象
OverClass<Boolean> over1 = new OverClass<Boolean>();
//实例化一个float类型的对象
OverClass<Float> over2 = new OverClass<Float>();
over1.setOver(true);
over2.setOver(2.0f);
Boolean b = over1.getOver();
Float f = over2.getOver();
System.out.println(b);
System.out.println(f);
}
}
对 class OverClass< T > 的理解:泛型类,也就是说这个类具体时什么类型是不确定的,也就是说 OverClass 可以是 String、可以是 Float、可以是 Boolean 也可以是自定义的 Apple 类型。他到底是什么类型要在创建这个类的对象的时候确定。如:
OverClass<String> str = new OverClass<String>();
这里,OverClass相当于String。
从泛型类派生子类
当创建了带有泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类来派生子类,但值得指出的是,当使用这些接口、父类时不能再包含类型形参,如下面代码就是错误的:
//类、接口中的类型形参:只有在定义类、接口时才可以使用类型形参,当使用类、接口时应为类型形参传入实际的类型(***)
public class A extends Apple<T>{}
并不存在泛型类
前面提到我们可以把 ArrayList 类当作是 ArrayList 的子类,事实上 ArrayList 类也确实是一种特殊的 ArrayList 类,这个 ArrayList 对象是能添加 String 对象作为集合元素。但实际上,系统并没有为 ArrayList 生成新的 class 文件,而且也不会把 ArrayList 当作新类来处理。
public class Test {
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<Double> list2 = new ArrayList<Double>();
//调用 getClass() 方法来比较 list1 和 list2 的类是否相等
System.out.println(list1.getClass() == list2.getClass());
}
}
//结果返回 true
上述代码输出的结果为 true ,因为不管泛型类型的实际类型参数是什么,它们在运行时总有同样的类。
因为类的静态变量和方法在所有实例间共享,所以在静态方法、静态初始化或者静态变量的声明或初始化中不允许使用类型形参。