类型转换异常 sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class

现象

1
2
3
4
5
6
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<R> requestClass = (Class<R>) parameterizedType.getActualTypeArguments()[0];
...
}

第四行 Class<R> requestClass = (Class<R>) parameterizedType.getActualTypeArguments()[0]java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class 错误

这个代码是子类想要拿到泛型参数时出现的类型转换异常

分析

上述问题中的类结构简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Request {}
public class Request2 extends Request {}
public class Request3 extends Request2 {}

public abstract class A<R extends Request> {

public void requestHandle {
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class requestClass = (Class) parameterizedType.getActualTypeArguments()[0];
...
}
}
}

public class B<R extends Request2> extends A<R> {
}

public class C extends B<Request3> {
}

1/ (new C).requestHandle()
因为 C 继承 B,所以 parameterizedType.getActualTypeArguments()[0] 拿到的其实就是 Request3.class

2/ (new B).requestHandle()
这种情况 parameterizedType.getActualTypeArguments()[0] 返回的是 TypeVariable,不是 Class
image.png
很好理解,因为 B 类的定义是 B,这里很容易因为泛型的类型擦除误认为 parameterizedType.getActualTypeArguments()[0] 会返回 Request2.class

解决

仔细观察 TypeVariable 接口定义

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
/**
* 类型变量
*/
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {

/**
* 获取上边界
* 例:B<R extends Request2> 返回 Request2.class
*/
Type[] getBounds();

/**
* 获取泛型声明的类型,即这个类型变量的声明来源
* 例:B<R extends Request2> 返回 B.class
*/
D getGenericDeclaration();

/**
* 获取该类型变量名称
* 例:B<R extends Request2> 返回 R
*/
String getName();

/**
* 带注解的上边界
*/
AnnotatedType[] getAnnotatedBounds();
}

我们可以调用 getBounds() 来获取泛型的上边界

最终调整如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void requestHandle {
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type actualTypeArgument = type.getActualTypeArguments()[0];

if (actualTypeArgument instanceof Class) {
Class requestClass = (Class) parameterizedType.getActualTypeArguments()[0];
...
} else if (actualTypeArgument instanceof TypeVariable) {
TypeVariable v = (TypeVariable) actualTypeArgument;
Class requestClass = (Class) v.getBounds()[0];
...
}
...
}
}