Java 泛型(generics)是JDK1.5中引入的一个新特性,其本质是参数化类型,解决不确定具体对象类型的问题;其所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

但是在Java中并不是真正的泛型,实际上是“伪泛型”

类型擦除(type Erasure)

为了与之前的版本兼容,JDK1.5中通过类型擦除来增加的泛型功能。Java泛型只是在编译器层次上,在编译后生成的字节码中是不包含泛型中类型的信息的。
通过一个例子来证明类型擦除

1
2
3
4
5
6
7
8
public class main {

public static void main(String[] args) {
ArrayList<String> sList = new ArrayList<String>();
ArrayList<Integer> iList = new ArrayList<Integer>();
System.out.println(sList.getClass() == iList.getClass());
}
}

上面定义了两个ArrayList,一个是ArrayList<String>泛型类型的,一个是ArrayList<Integer>类型的,但是最后打印的是true,说明两个类型相同。
javap -c看一下生成的生成的字节码

1

可以看到在字节码中,ArrayList<String>和ArrayList<Integer>都被编译成了ArrayList类型,可见编译后发生了类型擦除。

  1. 既然编译后发生了类型擦除,那么虚拟机解析、反射等场景是怎么获取到正确的类型的?

在JDk1.5中增加泛型的同时,JCP组织修改了虚拟机规范,增加了SignatureLocalVariableTypeTable新属性。
javap -v查看一下字节码,在main方法中包含一段

1
2
3
4
LocalVariableTypeTable:
Start Length Slot Name Signature
8 31 1 sList Ljava/util/ArrayList<Ljava/lang/String;>;
16 23 2 iList Ljava/util/ArrayList<Ljava/lang/Integer;>;

LocalVariableTypeTable是一个可选属性,如果存在泛型,则会出现这个属性。在Signature下包含了泛型的信息。

  1. 接下来,看这段代码
1
2
3
ArrayList<String> sList = new ArrayList<String>();
sList.add("111");
String s = sList.get(0);

类型擦除之后,当调用sList.get(0)是如何确保返回的值不会和String不匹配呢?
javap -c查看一下字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class com.example.demo.test.main {
// .....省略
public static void main(java.lang.String[]) throws java.lang.NoSuchFieldException;
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 111
11: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
14: pop
15: aload_1
16: iconst_0
17: invokevirtual #6 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
20: checkcast #7 // class java/lang/String
23: astore_2
24: return
}

#7处有一个checkcast指令,checkcast用于检查类型强制转换是否可以进行,也就是泛型在获取值的时候进行了强制类型转换。

  1. 再来看看下面这段代码

首先定义一个Java泛型类

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericClass<T> {

private T value;

public T getValue() {
return value;
}

public void setValue(T value) {
this.value = value;
}
}

再定义一个子类继承它

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericClassTest extends GenericClass<Integer> {

@Override
public void setValue(Integer value) {
super.setValue(value);
}

@Override
public Integer getValue(){
return super.getValue();
}
}

GenericClassTest中将GenericClass的泛型定义为Integer类型,并重写了get和set方法,因为存在类型擦除,父类GenericClass的泛型被擦除了。
javap -c 查看一下GenericClass编译后的字节码

2

可以看到类型擦除后泛型变为了Object。那么GenericClass也就变为了

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericClass {

private Object value;

public Object getValue() {
return value;
}

public void setValue(Object value) {
this.value = value;
}
}

这样,父类GenericClass中set和get方法操作的是Object对象,而子类GenericClassTest 操作的是Integer对象,为什么还可以重写?按照正常的继承关系中,这应该是重载。
按照重载的方式试一下

3

可以看到设置Object对象出现了红波浪线,不允许这样设置,看来确实是重写,而不是重载。为什么会时重写,这不是跟Java多态冲突么?继续往下研究。
现在用javap -c看一下子类GenericClassTest的字节码文件

4

GenericClassTest中get和/set方法都有两个,一个是操作Object对象一个是操作Integer对象。
操作Integer对象的是GenericClassTest定义的,操作Object对象的是由编译器生成的。
再用javap -v 查看一下字节码更详细的信息。

5

编译器生成的两个操作Object对象的方法中多了两个ACC_BRIDGEACC_SYNTHETIC标志。
这就是虚拟机解决类型擦除和多态冲突问题的方法:使用桥接方法
桥接方法方法是由编译器生成的,我们在代码中并不能直接使用,但是可以通过反射拿到桥接方法再使用。

泛型一旦编译过后,类型就被擦除了,那到了运行时,怎么获取泛型信息?这就要使用JDK提供的Type类型接口了。

Type类型

在没有泛型之前,所有的类型都通过Class类进行抽象,Class类的一个具体对象就代表了一个类型。
在JDK1.5增加了泛型之后,扩充了数据类型,将泛型也包含了。
JDK在原来的基础上增加了一个Type接口,它是所有类型的父接口,它的子类有

  • Class类: 原始/基本类型,包括平时我们所有的类、枚举、数组、注解,还有int、float等基本类型
  • ParameterizedType接口:参数化类型,比如List<String>
  • TypeVariable接口:类型变量,比如List<T>中的T就是参数化变量
  • GenericArrayType接口: 数组类型,比如List<String>[]、T[]
  • WildcardType接口:泛型表达式类型,比如List< ? extends Number>

ParameterizedType

参数化类型,即带有参数的类型,也就是带有<>的类型

1
2
3
4
5
6
7
public interface ParameterizedType extends Type {
Type[] getActualTypeArguments();

Type getRawType();

Type getOwnerType();
}
  • getActualTypeArguments(): 获取类型内部的参数化类型 比如Map<K,V>里面的K,V类型。
  • getRawType(): 类的原始类型,比如Map<K,V>中的Map类型。
  • getOwnerType(): 获取所有者类型(只有内部类才有所有者,比如Map.Entry他的所有者就是Map),若不是内部类,此处返回null。

实例:

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
public class GenericClass<T> {
private List<String> list;
private List<T> tList;

public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof ParameterizedType) {
System.out.println("==========" + genericType.getTypeName() + "======ParameterizedType类型=====");
ParameterizedType parameterizedType = (ParameterizedType) genericType;
System.out.println("getActualTypeArguments:");
Type[] actualTypeArguments = (parameterizedType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(" " + actualTypeArgument);
}
Type rawType = (parameterizedType).getRawType();
System.out.println("getRawType:");
System.out.println(" " + rawType);

}
}
}
}

输出

1
2
3
4
5
6
7
8
9
10
11
==========java.util.List<java.lang.String>======ParameterizedType类型=====
getActualTypeArguments:
java.lang.String
getRawType:
interface java.util.List
==========java.util.List<T>======ParameterizedType类型=====
getActualTypeArguments:
T
getRawType:
interface java.util.List

TypeVariable

类型变量,即泛型中的变量,例如:T、K、V等变量,可以表示任何类;

注意: 与ParameterizedType的区别,TypeVariable代表着泛型中的变量,而ParameterizedType则代表整个泛型。比如List<T>中,T是TypeVariable类型,List<T>是ParameterizedType类型

1
2
3
4
5
6
7
8
9
10
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {

Type[] getBounds();

D getGenericDeclaration();

String getName();
// JDK8新增的
AnnotatedType[] getAnnotatedBounds();
}
  • getBounds():类型对应的上限,默认为Object 可以有多个。比如List< T extends Number & Serializable>中的Number和Serializable
  • getGenericDeclaration(): 获取声明该类型变量实体,比如GenericClass< T>中的GenericClass
  • getName():获取类型变量在源码中定义的名称;

实例:

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
public class GenericClass<T extends Number> {
private T t;

public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof TypeVariable) {
System.out.println("==========" + genericType.getTypeName() + "======TypeVariable类型=====");
TypeVariable typeVariable = (TypeVariable) genericType;
Type[] bounds = typeVariable.getBounds();
System.out.println("getBounds:");
for (Type bound : bounds) {
System.out.println(" " + bound);
}
System.out.println("getGenericDeclaration:");
System.out.println(" " + typeVariable.getGenericDeclaration());
System.out.println("getName:");
System.out.println(" " + typeVariable.getName());


}
}
}
}

输出:

1
2
3
4
5
6
7
==========T======TypeVariable类型=====
getBounds:
class java.lang.Number
getGenericDeclaration:
class com.example.demo.test.GenericClass
getName:
T

GenericArrayType

泛型数组类型,用来描述ParameterizedType、TypeVariable类型的数组;例如:List<T>[] 、T[]、List<String>[]等。

注意: GenericArrayType是来描述与泛型相关的数组,与String[]、int[]、float[]这种类型不同。

1
2
3
4
public interface GenericArrayType extends Type {

Type getGenericComponentType();
}
  • getGenericComponentType():返回泛型数组中元素的Type类型,比如List<String>[] 中的 List<String>

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class GenericClass<T extends Number> {

private List<String>[] lists;
private T[] ts;

public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();

if (genericType instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) genericType;
System.out.println("==========" + genericType.getTypeName() + "======GenericArrayType类型=====");
Type genericComponentType = genericArrayType.getGenericComponentType();
System.out.println("getGenericComponentType:");
System.out.println(" " + genericComponentType);
}
}
}
}

输出:

1
2
3
4
5
6
==========java.util.List<java.lang.String>[]======GenericArrayType类型=====
getGenericComponentType:
java.util.List<java.lang.String>
==========T[]======GenericArrayType类型=====
getGenericComponentType:
T

WildcardType

泛型表达式(通配符表达式)。例如:? extend Number、? super Integer。

注意: WildcardType虽然是Type的子接口,但不代表一种类型,,表示的仅仅是类似 ? extends T、? super K这样的通配符表达式。

1
2
3
4
5
6
public interface WildcardType extends Type {

Type[] getUpperBounds();

Type[] getLowerBounds();
}
  • getUpperBounds() 获得泛型表达式上界(上限) 获取泛型变量的上边界(extends)
  • getLowerBounds() 获得泛型表达式下界(下限) 获取泛型变量的下边界(super)

实例:

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
27
28
29
30
31
32
33
34
35
36
public class GenericClass<T extends Number> {
private List<? extends Number> numbers;

private List<? super Integer> integers;

public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;

Type[] actualTypeArguments = (parameterizedType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
if(actualTypeArgument instanceof WildcardType){
System.out.println("==========" + actualTypeArgument.getTypeName() + "======WildcardType类型=====");
WildcardType wildcardType = (WildcardType) actualTypeArgument;
System.out.println("getUpperBounds:");
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type upperBound : upperBounds) {
System.out.println(" "+ upperBound);
}
System.out.println("getLowerBounds:");
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type lowerBound : lowerBounds) {
System.out.println(" "+ lowerBound);
}

}
}
}

}
}
}

输出:

1
2
3
4
5
6
7
8
9
10
==========? extends java.lang.Number======WildcardType类型=====
getUpperBounds:
class java.lang.Number
getLowerBounds:
==========? super java.lang.Integer======WildcardType类型=====
getUpperBounds:
class java.lang.Object
getLowerBounds:
class java.lang.Integer