1 什么是泛型
泛型即参数化类型,即是将类型由原本的类型参数化,类似于方法中的变量参数,此时类型也可以当作参数传入(可以称之为类型参数)。
2 为什么要使用泛型
先看一段代码:
List list = new ArrayList();
list.add("Acey");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); //取出Integer时,运行时出现异常
System.out.println("name:" + name);
}
当我们在list
中插入一个字符串和整数时是合法的,因为list
默认的泛型即为Object
类型。但是当我们从集合中取出数据时,需要对不同的类型进行强转成符合的对应的类型。为了解决这一问题,泛型就出现了。
3 泛型的使用
- Java泛型变成时在jdk1.5版本后引出的,使用的时候注意了。。
- 泛型只在编译阶段有用,在编译的过程中,将会正确的检验泛型的结果,将泛型的相关信息擦除,在对象进入和离开方法的边界时会加上对应的类型转换方法。也就是说在成功编译成class文件后将不会在包含任何泛型的信息。
4 泛型方法
对于常见的泛型模式,推荐的名称:
K - 键
V - 值
E - 异常
T - 泛型
泛型方法使得该方法能独立于类而产生变化。以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数。所以,如果static方法需要使用泛型能力,就必须使其成为泛型方法。
要定义泛型方法,只需将泛型参数列表置于返回值之前,就像下面这样:
public <T> void f(T x){
System.out.println(x.getClass().getName());
}
可变参数与泛型方法
public static <T> List<T> makeList(T... args){
List<T> result = new ArrayList<T>();
for(T item:args)
result.add(item);
return result;
}
5 构建一个复杂的元组
元组和``list```列表一样,都是用来存储数据,但是和列表不同的是,列表只能存储相同的数据类型,而元组可以存储多种数据类型,且可以根据自己的需求无线扩展。
class ThreeTuple2<A,B,C>{
public final A first;
public final B second;
private final C three;
public ThreeTuple2(A a,B b,C c){
first = a;
second = b;
three = c;
}
public String toString(){
return "(" + first + "," + second + "," + three + ")";
}
}
6 泛型通配符 “ ? ”
在不确定使用哪种类型的时候可以使用泛型通配符 “? ”来代替,但是在操作该类型的对象时,只能使用Object
中的功能。
Class<?>classType = Class.forName("java.lang.String");
7 泛型的上限和下限
上限:? extends E 可以接收 E 类型或E类型以下的类型对象
上限:? super E 可以接收E类型或E类型以上的类型对象。
7 泛型擦除
java 泛型是用泛型擦除来实现的,类型擦除就是说Java泛型只能用于在编译期间的静态类型检查,然后编译器生成的代码会擦除相应的类型信息,这样到了运行期间实际上JVM根本就知道泛型所代表的具体类型。这样做的目的是因为Java泛型是1.5之后才被引入的,为了保持向下的兼容性,所以只能做类型擦除来兼容以前的非泛型代码
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
}
编译器在做了相应的检查后,在运行期间上面的代码会转换为
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
这也就是说,我们若只是给定了泛型参数 T 的话,那么不管我们传进来的是 Integer 还是 String .... 最终都会被当作 Object 来看待。
如果我们想要设置自己的 bounds ,那么我们就应该使用上限来设置。
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}