抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

啥是泛型

概述

泛型即参数化类型,Java泛型( generics) 是JDK 5中引⼊的⼀个新特性, 允许在定义类和接⼜的时候使⽤类型参数( type parameter) 。
声明的类型参数在使⽤时⽤具体的类型来替换。 泛型最主要的应⽤是在JDK 5中的新集合类框架中。

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ExampleGenerics {
public static void main(String[] args) {

//java1.5以后写法
// 使用泛型集合
List<Integer> integerList = new ArrayList<>();
for (int i = 0;i < 10;i++){
integerList.add(i);
}
for (Integer integer:integerList){
System.out.println(integer);
}
//java1.5之前写法
//需要强转
List list = new ArrayList();
for (int i = 0;i < 10;i++){
list.add(i);b
}
for (Object o:list){
System.out.println((Integer)o);
}
}
}

类型擦除

各语言是如何处理泛型特性的

一般来说,泛型可以分为两种

  1. Code specialization。在实例化一个泛型类或泛型方法时都产生一份新的目标代码(字节码or二进制代码)。例如,针对一个泛型list,可能需要 针对String,Integer,Float产生三份目标代码。代表语言(c++,c#)
  2. Code sharing。对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。代表语言(java)

Java编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

啥是类型擦除

java编译器首先找到泛型的具体类也就是上界,这个类一般是Object。然后将泛型替换成具体类,擦除所有类型参数。

编译器处理泛型的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//源代码
interface Comparable<A> {
public int compareTo(A that);
}

public final class NumericValue implements Comparable<NumericValue> {
private byte value;

public NumericValue(byte value) {
this.value = value;
}

public byte getValue() {
return value;
}

public int compareTo(NumericValue that) {
return this.value - that.value;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//反编译后
interface Comparable {
public int compareTo( Object that);
}

public final class NumericValue
implements Comparable
{
public NumericValue(byte value)
{
this.value = value;
}
public byte getValue()
{
return value;
}
public int compareTo(NumericValue that)
{
return value - that.value;
}
public volatile int compareTo(Object obj)
{
return compareTo((NumericValue)obj);
}
private byte value;
}

可以发现Comparable被编译器擦除后替换成具体类Object,Comparable的类型参数被擦除后,导致NumericValue没有实现接口Comparable的compareTo(Object that)方法 。所以编译器生成了一个桥接方法。

泛型常用字母含义

E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型(无限制通配符类型)

通配符和上下界

使用泛型类可以指定具体类型,也可以使用?通配符来表示未知类型。一般我们会使用上下界来限制通配符。

<? extends T>和<? super T>是Java泛型中的“通配符(Wildcards)”和“边界(Bounds)”的概念。
<? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”,即泛型中的类必须为当前类的子类或当前类。
<? super T>:是指 “下界通配符(Lower Bounds Wildcards)”,即泛型中的类必须为当前类或者其父类。

1
2
3
4
5
6
7
8
9
10
11
12
public static void TestExtends(){
List<? extends Number> list = new ArrayList<>();
list.add(new Integer(1));//报错
Number number = list.get(0);//返回上界
}

public static void TestSuper(){
List<? super Number> list = new ArrayList<>();
list.add(new Integer(1));
list.add(new Object());//报错
Object o =list.get(0);//只能返回Object类型
}

从上面的例子我们可以看出两个通配符的不同特性。
在TestExtends中我们不能确定list中的具体类型,可能是Integer或者Float等。因此使用add方法都会出现编译错误。
在TestSuper中我们不能确定元素的上界是什么,所以只能返回Object类型。
在java中有一个PECS原则Producter Extends,Consumer Super,意思就是如果需要获取元素(生产)使用extends通配符,如果要添加元素(消费)使用super通配符。