原创

【每天十分钟JAVA快速入门】(十三)泛型

泛型

在使用泛型之前为了让代码可以处理多种类型的对象,我们常常会接收一个Object类型的参数,然后再进行强制转型,这样的代码在可读性和安全性方面都很差,方法的调用者不知道我们究竟可以处理哪些类型,强制转型也常常会带来类转换异常。

类型参数
在引入泛型之前,我们可以往ArrayList里添加任意类型的元素,例如:
ArrayList list = new ArrayList();
list.add("aString");
list.add(100);
在获取元素值时必须进行强制类型转换,一旦转换错误,程序运行就会出错,例如:
String value = (String)list.get(1);
这里就会抛出异常:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
引入泛型后,我们可以给ArrayList类制定一个类型参数如下:
ArrayList<String> list = new ArrayList();
这样代码可读性和安全性都有了保障,一眼就能看出ArrayList里的元素类型,获取元素时也不必再使用强制类型转换了。

定义泛型类
泛型类就是拥有一个或多个类型参数的类,例如:
public class Generic<T> {
private T element;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
}
类型参数使用尖括号(<>)括起来,可以有多个类型参数,例如<T,U>使用逗号(,)隔开即可。
类型参数一般使用单个大写字母表示,例如E表示集合类型,K和V表示Key-Value类型,T、U、S表示任意类型。
实例化一个泛型类只需要在实例化时指定具体的类型参数即可,例如:
Generic<String> generic = new Generic<>();

泛型方法
泛型方法就是带有类型参数的方法,例如:
public class JsonUtil{
public static <T> T fromJson(String jsonStr , Class<T> clazz){
Gson gson = new Gson();
return gson.fromJson(jsonStr,clazz);
}
}
这个静态方法使用Gson将json字符串转换为指定类型的对象。泛型方法不一定要定义在泛型类中,这里的泛型方法就定义在一个普通类中。

类型参数的限定
有时候我们需要限定类型参数的范围而不是全部类型,假设需要定义一个方法将类型参数限制为所有实现了Comparable接口的类,可以使用extends关键字来实现,例如:
public static <T extends Comparable> T sample(T t)
甚至还可以指定多个限制,使用&来连接,例如:
public static <T extends Comparable & Cloneable> T sample(T t)

通配符类型
?通配符,泛指所有类型。一般用于定义一个泛型的引用变量,可以指向类型参数不同的泛型类对象,例如:
Generic<?> generic = new Generic<String>();
generic = new Generic<Integer>();
通配符类型限定于类型参数的限定相似,例如:
Generic<? extends Parent> generic = new Generic<Child>();
但是它还有另一个能力,就是可以使用super关键字来限制类型为超类,例如:
Generic<? super Child> generic = new Generic<Parent>();

类型擦除
虚拟机里是不存在泛型对象的,所有实例化的对象都是一个确定类型的普通类对象,任何泛型类都会自动提供一个原始类型,也就是去掉了类型参数,使用限定的类型,如果是T这样的无限定类型则使用Object来代替,称之为类型擦除,例如上面定义的泛型类Generic<T>,类型擦除如下:
public class Generic {
private Object element;
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
}

约束与限制
泛型的使用有一定的约束与限制,而这些大多与类型擦除有关。
类型参数不能是基本类型,这个很好理解,试想如果类型擦除后的类含有Object类型的成员变量,就会发生错误,因为基本数据类型不能转成Object。
不能使用instanceof来检查泛型类实例,例如:
if(o instanceof Generic<String>) // 这是一个错误的语句。
不支持泛型类型数组,例如:
Generic<String>[] genericArray = new Generic<>[10]; // 这是一个错误的语句。
不支持泛型静态成员变量和方法。
正文到此结束