介绍java泛型的定义以及实际的具体使用方法。
什么是泛型?
泛型是JDK5引入的新特性,本质是参数化类型。在定义的时候无需知道参数的具体类型,即将参数类型推迟到使用时决定。
这种参数类型可以体现在类,接口,方法中,我们可以称呼为泛型类,泛型接口,泛型方法。
为什么使用泛型?
-
保证类型安全
举个例子,我们都知道List只能存放同种类型的参数,在没有泛型之前是无法得知不同类型的参数存放到同一个list中是否报错
/** * 泛型出现之前 */ List list = new ArrayList(); // 本意是想在该list中存放字符串类型的参数 list.add("测试"); // 由于没有泛型指定参数类型,同样编译通过 list.add(1);
/** * 泛型出现之后 */ List<String> list = new ArrayList<>(); // 本意是想在该list中存放字符串类型的参数 list.add("测试"); // 编译失败 list.add(1);
-
消除强制转换
如果没有泛型,无法得知List中存放的具体是某种参数类型,所以无法避免强制转换。
/** * 泛型出现之前 */ List list = new ArrayList(); // 本意是想在该list中存放字符串类型的参数 list.add("测试"); // 必须通过强制转换 String a = (String) list.get(0);
/** * 泛型出现之后 */ List<String> list = new ArrayList<>(); // 本意是想在该list中存放字符串类型的参数 list.add("测试"); // 无需强制转换 String a = list.get(0);
-
避免重复拆装箱操作
-
提高代码可用性
泛型使用
泛型命名规范,一般都是使用单个大写字母来指定,例如T或者R等等。
泛型类
泛型定义在类上,定义格式如下:
public class ClassName <T> {
}
我们模拟一个场景,小朋友写数字,有些小朋友写的int类型的阿拉伯数字,有些小朋友写的是字符串的中文数字,于是我们定义一个Child类,并指定一个泛型T来接收小朋友报的数:
public class Child <T> {
private T value;
// 定义一个泛型T构造函数
public Child(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args){
Child<String> childOne = new Child<>("一");
// 1号小朋友写的是字符串类型的中文数字一
System.out.println(childOne.getValue());
Child<Integer> childTwo = new Child<>(1);
// 2号小朋友写的是字符串的int类型的阿拉伯数字1
System.out.println(childTwo.getValue());
}
泛型接口
泛型定义在接口中,例如:
public interface Test<T>{
void test(T value);
}
泛型方法
泛型定义在方法上,定义格式如下:
public <T> T test(T t){
return t;
}
举例,接收同种类型的两个参数,输出不为null的结果:
public static <T> T firstNonNull(@CheckForNull T first, T second) {
if (first != null) {
return first;
}
if (second != null) {
return second;
}
throw new NullPointerException("Both parameters are null");
}
该例子取自Google的guava源码。
扩展
对于泛型经常使用的还有泛型通配符,有以下三种使用方式:
- <?>无边界的泛型
- <? extends E>上边界泛型
- <? super E>下边界泛型
public class MyClass <?>{
}
public class MyClass <T extends XxxClass>{
}
public class MyClass <T super XxxClass>{
}
源码中的泛型通常使用E,K,V等,实际上他们都是有实际含义的:
E:Element (在集合中使用,因为集合中存放的是元素)
T:Type(Java 类)
K:Key(键)
V:Value(值)
N:Number(数值类型)
?:表示不确定的java类型
那么我们再思考一个问题,泛型具体是如何实现的呢。
我们都知道Object是所有类的父类,那么实际上泛型就是当你定义的时候用Object来接收,然后通过尖括号里定义的泛型类型来进行类型限定,例如String或者Integer等
除非是使用了泛型通配符,例如,那么会使用Aclass来接收。
参考资料: