JAVA获取类的泛型信息

一、前言

在我写 SpringBoot JPA 通用controller 的过程中,发现可以直接使用 Entitymanager进行实体类的操作,EntityManager需要实体信息,因为第一次不熟悉EntityManager
的操作,所以要获取当前类的泛型信息,然后传入EntityManager完成通用的接口操作。

二、以Dao为例

实体接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Data
@Accessors(chain = true)
@MappedSuperclass//实体集成映射
public class BaseEntity implements Serializable {
@Id
@Column(name = "ID")
@ApiModelProperty(value = "主键")
private Long id;

@Basic
@Column(name = "DEL_STATUS")
@ApiModelProperty(value = "状态 1启用 0 停用")
private Integer delStatus;
}

实体

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
/**
* 应用接口日志
*
* @author author
* @date 2019-7-23 15:38:57
*/
@Accessors(chain = true)
@Data
@Entity
@Table(name = "app_log_api", schema = "", catalog = "")
@ApiModel(value = "应用接口日志", description = "应用接口日志")
public class AppLogApi extends BaseEntity implements Serializable {
private static final long serialVersionUID = -1L;

@Basic
@Column(name = "API")
@ApiModelProperty(value = "请求地址")
private String api;

@Basic
@Column(name = "API_CLASS")
@ApiModelProperty(value = "请求的类.方法")
private String apiClass;

/*****/
public AppLogApi() {
}
}

Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14

/**
* BaseService
*
* @author maxzhao
* @date 2019-08-15 11:46
*/
public interface BaseService<T extends BaseEntity, ID> {
/**
* 逻辑删除
**********
*/
ResultObj<String> del(ID id);
}

Dao实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@Slf4j
@Service(value = "baseService")
public class BaseServiceImpl<T extends BaseEntity, ID> implements BaseService<T, ID> {
private Class<T> clazz = null;

{
Type type = this.getClass().getGenericInterfaces()[0];
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type claz = pType.getActualTypeArguments()[0];
if (claz instanceof Class) {
this.clazz = (Class<T>) claz;
}
}
}

ResultObj<String> del(ID id) {
return null;
}
}
1
2
3
4
5
6
7
8
9
10
{
Type type=getClass().getGenericSuperclass();
if(type instanceof ParameterizedType){
ParameterizedType pType=(ParameterizedType)type;
Type claz=pType.getActualTypeArguments()[0];
if(claz instanceof Class){
this.clazz=(Class<T>)claz;
}
}
}

其它解读

一、Type接口
Type是所有类型的父接口,他有4个子接口和一个实现类。

Class比较常见,它表示的是原始类型。Class类的对象表示JVM中的一个类或接口。每个Java类在JVM里都表现为一个Class对象,可以通过“类名.class”、“对象.getClass()”、“Class.forName(“类名”)
”等方式获取Class对象。数组也被映射为Class对象,所有元素类型相同且维数相同的数组都共享同一个Class对象。
ParameterizedType表示的是参数化类型,例如List、Map<Integer,String>、Service这种带有泛型的类型。
ParameterizedType接口中常用的方法有3个,分别是:
(1) Type getRawType()——返回参数化类型中的原始类型,例如List的原始类型为List。
(2) Type[] getActualTypeArguments()——获取参数化类型的类型变量或是实际类型列表,例如Map<Integer,String>
的实际泛型列表是Integer和String。需要注意的是,该列表的元素类型是Type,也就是说,可能存在多重嵌套的情况。
(3) Type getOwerType()——返回的是类型所属的类型,例如存在A,其中定义了内部类InnerA,则InnerA所属的类型是A,如果是顶层类型则返回null。这种关系常见与Map<K,V>
接口与Map.Entry<K,V>接口,Map<K,V>是Map.Entry<K,V>接口的所有者。
TypeVariable表示的是类型变量,它用来反映的是JVM编译该泛型前的信息,例如List中的T就是类型变量,它在编译时需要被转换为一个具体的类型后才能正常使用。
该接口常用的方法有3个,分别是:
(1) Type[] getBounds()——获取类型变量的上边界,如果未明确声明上边界则默认为Object。例如Class中K的上边界就是Person。
(2) D getGenericDeclaration()——获取声明该类型变量的原始类型,例如Test中原始类型是Test。
(3) String getName()——获取在源码中定义的名字,上例中为K。
GenericArrayType表示的是数组类型且组成元素时ParameterizedType或TypeVariable,例如List或T[],该接口只有Type getGenericComponentType()
一个方法,它返回数组的组成元素类型。
WildcardType表示的通配符类型,例如? extends Number 和 ? super Integer。
Wildcard接口有两个方法,分别是:
(1) Type[] getUpperBounds()——返回类型变量的上边界。
(2) Type[] getLowerBounds()——返回类型变量的下边界。
二、ParameterizedType与TypeVariable的使用
1.未指定泛型参数的泛型类(例如:ArrayList
public class TypeTest {
public static void main(String[] args) {
System.out.println(“接口是否是泛型类:”+ (ArrayList.class.getGenericInterfaces()[0] instanceof ParameterizedType));
System.out.println(“泛型类的名称:”+ ArrayList.class.getGenericInterfaces()[0].getTypeName());
System.out.println(“泛型类的实现:”+ ArrayList.class.getGenericInterfaces()[0].getClass());
System.out.println(“是否是泛型参数:”+(((ParameterizedType)ArrayList.class.getGenericInterfaces()[0])
.getActualTypeArguments()[0] instanceof TypeVariable));
System.out.println(“泛型参数名称:”+((ParameterizedType)ArrayList.class.getGenericInterfaces()[0]).getActualTypeArguments()[0]
.getTypeName());
System.out.println(“泛型参数的实现:”+((ParameterizedType)ArrayList.class.getGenericInterfaces()[0]).getActualTypeArguments()[0]
.getClass());
}
}

//输出结果:
接口是否是泛型类:true
泛型类的名称:java.util.List
泛型类的实现:class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
是否是泛型参数:true
泛型参数名称:E
泛型参数的实现:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这里,我们使用的是ArrayList.class,如果使用new ArrayList().getClass(),是否得到的泛型参数就是String呢?答案是否定的,因为this.getClass()
对象是被所有的不同具体类型的ArrayList实例 共享的(例如:new ArrayList(), new ArrayList() 等),所以在字节码中类型会被擦除到上限。

2.指定泛型参数的泛型类(例如:GenericSubClass
public class GenericClass {
}

public class GenericSubClass1 extends GenericClass {
}

public class GenericSubClass2 extends GenericClass {
}

public class TypeTest {
public static void main(String[] args) {
Class clazz = GenericSubClass1.class;
//Class clazz = GenericSubClass2 .class;
//Class clazz = new GenericSubClass1().getClass();
//Class clazz = new GenericSubClass2().getClass();
System.out.println(“父类是否是泛型类:”+ (clazz.getGenericSuperclass() instanceof ParameterizedType));
System.out.println(“泛型类的名称:”+ clazz.getGenericSuperclass().getTypeName());
System.out.println(“泛型类的实现:”+ clazz.getGenericSuperclass().getClass());
System.out.println(“是否是泛型参数:”+(((ParameterizedType)clazz.getGenericSuperclass()).getActualTypeArguments()[0] instanceof
TypeVariable));
System.out.println(“泛型参数名称:”+((ParameterizedType)clazz.getGenericSuperclass()).getActualTypeArguments()[0]
.getTypeName());
System.out.println(“泛型参数的实现:”+((ParameterizedType)clazz.getGenericSuperclass()).getActualTypeArguments()[0].getClass());
}
}

//输出结果:
父类是否是泛型类:true
泛型类的名称:com.arch.test.reflect.GenericClass
泛型类的实现:class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
是否是泛型参数:true
泛型参数名称:String
泛型参数的实现:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl

不管是使用”类.class”,还是使用”对象.getClass()”,得到的父类的泛型参数始终是String。

本文地址: https://github.com/maxzhao-it/blog/post/56235/