泛型 Generic 概述
什么是泛型
泛型是 JDK1.5 引入的一种技术,用参数表示一个类型,如 T,K 等,在实例化的时候再指定其具体的类型,参数类型可以分为三种
- 泛型类
- 类定义时指定参数,即构成了泛型类,例如下面代码中就指定 T 为类型参数,那么在这个类里面就可以使用这个类型了。例如定义 T 类型的变量 name,申明 T 类型的形参 param 等操作
- 泛型接口
- 与泛型类相似
- 泛型方法
- 方法返回值前面使用
<>
标记类型
- 方法返回值前面使用
public class GenericBean<T, K> {
public T t;
public K k;
private Number number;
public <V extends Number> GenericBean(V v) {
number = v;
}
// 非泛型方法
public T get() {
return t;
}
// 泛型方法
public <X> X make(T t) {
return null;
}
// 泛型方法,并且隐藏了类中的泛型 T,通俗一点就是类的 T 不等同于这里的类型 T
public <T> T change(T t) {
return null;
}
public static void main(String[] args) {
GenericBean<Integer, Serializable> bean = new GenericBean<>();
// 编译不报错,可见 change 方法的 T 是 String,而类中的 T 为 Integer
bean.change("aaa");
}
}
为什么用泛型
在引入泛型之前,类型转换总是在运行期使用类型强转转换对象类型,但是如果不下心转换失败就会抛出 ClassCastException
异常,这些检查工作都由程序员把控
public class GenericTestOne {
public static void main(String[] args) {
List list = new ArrayList();
list.add("测试");
list.add(123);
for (Object element : list) {
if (element instanceof String) {
System.out.println("String 类型结果:" + element);
}
if (element instanceof Integer) {
System.out.println("Integer 类型结果:" + element);
}
}
}
}
// 输出结果
// String 类型结果:测试
// Integer 类型结果:123
使用泛型之后,可以把类型检查的工作提前到编译期,并且交给编译器去校验类型
public class GenericTestTwo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("测试");
// 编译不通过
list.add(123);
for (Object element : list) {
System.out.println("String 类型结果:" + element);
}
}
}
怎么使用泛型
泛型类的使用
- 如果子类没有在继承的时候选择泛型的类型,那么子类也要定义泛型,或者都不指定
public class GenericChildOne<T> extends GenericSuper<T>{ }
- 如果子类在继承的时候选择了泛型类型,那么子类无需定义泛型类型
public class GenericChildTwo extends GenericSuper<String> { }
泛型接口的使用
与泛型类一致
泛型方法的使用
与普通方法一样
通配符 ?
单独使用的时候其实与没有是一样的,也就默认是 Object 的,一般会与 extends 或者 super 合用表示泛型的上限或者下限
通配符上限
<? extends T>
表示泛型必须是 T 或者 T 的子类,一般用在生产方法中
通配符下限
<? super T>
表示泛型必须是 T 或者 T 的父类,一般用在消费方法中
泛型的原理
Java 用的是伪泛型,在编译的时候会把泛型 T 用 Object 代替,可以编译看编译后的 class 文件