什么是泛型
Java 在 1.5 时增加了泛型机制,据说专家们为此花费了 5 年左右的时间(听起来很不容易)。有了泛型之后,尤其是对集合类的使用,就变得更规范了。
看下面这段简单的代码。
ArrayList<String> list = new ArrayList<String>();
list.add("沉默王二");
String str = list.get(0);
你能想象到在没有泛型之前该怎么办吗?
在没有泛型之前,我们可以使用 Object 数组来设计 Arraylist 类
class Arraylist {
private Object[] objs;
private int i = 0;
public void add(Object obj) {
objs[i++] = obj;
}
public Object get(int i) {
return objs[i];
}
}
然后,我们向 Arraylist 中存取数据。
Arraylist list = new Arraylist();
list.add("沉默王二");
list.add(new Date());
String str = (String)list.get(0);
但是这样有两个问题
- Arraylist 可以存放任何类型的数据(既可以存字符串,也可以混入日期),因为所有类都继承自 Object 类。
- 从 Arraylist 取出数据的时候需要强制类型转换,因为编译器并不能确定你取的是字符串还是日期。
对比一下,你就能明显地感受到泛型的优秀之处:使用类型参数解决了元素的不确定性——参数类型为 String 的集合中是不允许存放其他类型元素的,取出数据的时候也不需要强制类型转换了。
泛型类型的创建
public class Wrapper<T> {
T instance;
public T get() {
return instance;
}
public void set(T newInstance) { instance = newInstance;
}
}
泛型接口的创建
public interface Shop<T> {
T buy();
float refund(T item);
}
泛型作用小结
- 帮助检查代码中的类型,提前报错;
- 自动强制转型。
「创建一个泛型类型」到底是为了什么
- 本质目标或原因:这个类型的不同实例的具体类型可能会有不同,针对的
是实例
- 因此,静态字段和静态方法不能使用泛型类型的类型参数(也就是那个
T
)
继承
public class AppleShop extends Shop<Apple> {
@Override
Apple buy();
@Override
float refund(Apple item);
}
public interface RealShop<T> extends Shop<T> {
@Override
public T buy() {
return null;
}
@Override
public float refund(Object item) {
return 0;
}
}
类型参数 <T> 到底是什么
- 不是一个类,也不是一个接口。只是一个标记符号,可以用其他字母代替
- 代表这个类型内部某个通用的类型
但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如:
T 代表一般的任何类。
E 代表 Element 的意思,或者 Exception 异常的意思。
K 代表 Key 的意思。
V 代表 Value 的意思,通常与 K 一起配合使用。
S 代表 Subtype 的意思,文章后面部分会讲解示意。
多个类型参数
public interface HenCoderMap<K, V> {
public void put(K key, V value);
public V get(K key);
}
public interface SimShop<T, C> extends Shop<T> {
T buy(float money);
float refund(T item);
C getSim(String name, String id);
}
泛型类型的实例化
- 所谓泛型实例化其实就是「确定这个 T 的实际值」的意思
// 左右两边的尖括号都是 ArrayList 的类型参数的实例化
ArrayList<Apple> apples = new ArrayList<>();
// 左边的 E 是 Repairable 的类型参数的声明;右边的 E 是 Shop 的类型参数的实例化
interface RepairableShop<E> extends Shop<E>{
}