如何获取全参数化泛型类型的Class引用?(Mockito测试场景)
首先得明确:你没法直接写出List<String>.class这种代码——这不是语法问题,而是Java的「类型擦除」机制导致的:在运行时,所有泛型参数都会被擦除,JVM里List<String>和List<Integer>共享同一个List.class,所以不存在所谓的「参数化类型的Class对象」。
但你的需求是在单元测试里用Mockito模拟TypedQuery,这其实不需要真正拿到参数化类型的Class,而是需要让Mockito识别泛型类型信息,这里有几种实用方案:
方案1:直接Mock + 类型推断(最简单场景)
如果只是要模拟TypedQuery并指定返回值,大部分情况下Mockito的类型推断已经足够,不需要额外处理泛型类型:
// 直接Mock TypedQuery,指定泛型参数 TypedQuery<List<YourEntity>> mockTypedQuery = Mockito.mock(TypedQuery.class); // 准备模拟结果 List<YourEntity> mockResult = Arrays.asList(new YourEntity(), new YourEntity()); // 指定调用getResultList()时返回模拟数据 Mockito.when(mockTypedQuery.getResultList()).thenReturn(mockResult);
这种写法在编译时会保留泛型信息,Mockito能正确处理返回值的类型匹配。
方案2:构造ParameterizedType(匹配方法参数场景)
如果你的场景是要模拟EntityManager.createQuery(String, Type)这种接受Type参数的方法(比如要返回TypedQuery<List<YourEntity>>),就需要手动构造ParameterizedType来描述参数化类型:
// 构造List<YourEntity>的ParameterizedType实例 ParameterizedType listEntityType = new ParameterizedType() { @Override public Type[] getActualTypeArguments() { // 泛型的实际参数:这里是YourEntity.class return new Type[]{YourEntity.class}; } @Override public Type getRawType() { // 原始类型:List.class return List.class; } @Override public Type getOwnerType() { // 没有外部类,返回null return null; } }; // Mock EntityManager和TypedQuery EntityManager mockEm = Mockito.mock(EntityManager.class); TypedQuery<List<YourEntity>> mockTypedQuery = Mockito.mock(TypedQuery.class); // 匹配createQuery的Type参数,返回Mock的TypedQuery Mockito.when(mockEm.createQuery(Mockito.anyString(), Mockito.eq(listEntityType))) .thenReturn(mockTypedQuery);
方案3:用Guava TypeToken简化代码(可选)
如果你的项目已经引入了Guava依赖,可以用TypeToken来避免手动实现ParameterizedType,代码会简洁很多:
// 通过匿名内部类保留泛型信息 TypeToken<TypedQuery<List<YourEntity>>> typeToken = new TypeToken<TypedQuery<List<YourEntity>>>() {}; // 获取ParameterizedType ParameterizedType typedQueryType = (ParameterizedType) typeToken.getType(); // 后续Mock步骤和方案2一致 TypedQuery<List<YourEntity>> mockTypedQuery = Mockito.mock(TypedQuery.class, Mockito.withSettings().type(typedQueryType));
核心原理总结
Java的泛型是「编译时语法糖」,运行时不存在参数化类型的Class对象,但我们可以通过ParameterizedType这个反射接口来描述参数化类型的结构——很多框架(包括Mockito)都支持用这个接口来识别泛型信息,从而实现类型匹配和模拟。
内容的提问来源于stack exchange,提问作者Ryan Pierce Williams

