ParameterizedType和isAssignableFrom

本文介绍了如何在Java运行时获取泛型的具体类型,通过分析Gson源码并提供了一个具体的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学过Java的人都知道,Java的泛型是编译时实现的,运行时泛型信息是被擦除的.但是我们为了解析Json数组就必须知道应该为给定的Class的List类型解析出什么样类型的对象以赋值.这个问题看上去是不可解决的了.但是因为我的接口设计是仿照Gson的,而Gson是可以实现这个功能的.带着疑问,我在Gson的代码中找到了这个.

<code class="language-Java hljs java has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ParameterizedTypeImpl</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ParameterizedType</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Serializable</span> {</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Type ownerType;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Type rawType;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Type[] typeArguments;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">ParameterizedTypeImpl</span>(Type ownerType, Type rawType, Type... typeArguments) {
      <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// require an owner type if the raw type needs it</span>
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (rawType <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">instanceof</span> Class<?>) {
        Class<?> rawTypeAsClass = (Class<?>) rawType;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers())
            || rawTypeAsClass.getEnclosingClass() == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
        checkArgument(ownerType != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> || isStaticOrTopLevelClass);
      }

      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.ownerType = ownerType == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> ? <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> : canonicalize(ownerType);
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.rawType = canonicalize(rawType);
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments = typeArguments.clone();
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> t = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; t < <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments.length; t++) {
        checkNotNull(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments[t]);
        checkNotPrimitive(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments[t]);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments[t] = canonicalize(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.typeArguments[t]);
      }
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li></ul>

事实上ParameterizedType就是我们需要寻找的带泛型的类啦.但是上面这段代码由于是在Gson的类型系统设计下,所以显得不是那么好懂.我搜索了其中的关键字后,在JPJson中实现了下面的代码.

<code class="language-Java hljs actionscript has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">Class fieldType = field.getType();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (fieldType.isAssignableFrom(List.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span>)) {
    ParameterizedType pt = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">null</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
        pt = (ParameterizedType) field.getGenericType();
    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (ClassCastException e) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"you must offer a genericType on the bean's list type for json array to parse"</span>);
    }
    Class cla = (Class) pt.getActualTypeArguments()[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];
    fieldType = cla;
                    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

上面这段代码中

  • 首先我使用field.getType()获取了给定Class参数的类型.
  • 然后使用fieldType.isAssignableFrom(List.class)判断该field是否是一个List.
  • 如果是List类型,下面关键的来了,我使用field.getGenericType()获取带泛型的List类型
  • 然后将其强转为ParameterizedType类型.
  • 这里要捕获一个类型强转的异常,因为如果对应的List没有填写泛型类型的话,那么这次的强转会出异常.这个异常是由于用户提供的泛型类没有带泛型类型导致的,而我们确实需要这个类型.所以我们重新抛出一个新的吟唱并且带上必要的提示信息.
  • ParameterizedType是包含了泛型的Field类型所以实际上它应该是java.util.List<java.lang.String>这种形式的类型.所以我们使用Class cla = (Class) pt.getActualTypeArguments()[0]这个方法来获取真正的泛型类型.

下次各位读者需要获取泛型的类型的话,就可以使用这种方法哦.

运行时获取泛型的原理

这里摘抄一段来自书上的话,这是我之前看这本书是看到过的,书上只是提供了原理,但是没说在代码中应该怎么使用,今天使用过后,算是对原理有了更深的理解.

由于泛型的引入,各种场景(虚拟机解析,反射等)下的方法调用都可能会对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等.因此JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature,LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型识别问题,Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型不是原生类型而是包括了参数化类型的信息.修改后的虚拟机规范要求所有能识别49.0以上版本的Class文件虚拟机都要能正确识别Signature参数.

package com.example.kucun2.DataPreserver; import android.util.Log; import com.example.kucun2.entity.data.SynchronizableEntity; import com.example.kucun2.entity.data.SynchronizedList; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class DataAssociator { private final DataStore dataStore; public DataAssociator(DataStore dataStore) { this.dataStore = dataStore; } public void automaticAssociation() { dataStore.dataCollectionMap.keySet().forEach(entityClass -> { try { associateEntities(dataStore.dataCollectionMap.get(entityClass)); } catch (Exception e) { Log.e("DataAssociator", "关联失败: " + entityClass.getSimpleName(), e); } }); } private void associateEntities(SynchronizedList<?> list) throws Exception { for (SynchronizableEntity entity : list.getViewList()) { if (entity == null) continue; for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Class<?> fieldType = field.getType(); if (SynchronizableEntity.class.isAssignableFrom(fieldType)) { associateSingleReference(entity, field); } else if (List.class.isAssignableFrom(fieldType)) { associateReferenceList(entity, field); } } } } private void associateSingleReference(SynchronizableEntity entity, Field field) throws Exception { SynchronizableEntity ref = (SynchronizableEntity) field.get(entity); if (ref == null) return; Class<?> targetType = field.getType(); SynchronizableEntity target = findTargetEntity(ref, targetType); field.set(entity, target); } private SynchronizableEntity findTargetEntity(SynchronizableEntity ref, Class<?> targetType) { SynchronizedList<?> targetList = dataStore.dataCollectionMap.get(targetType); if (targetList == null) return null; // 预置对象特殊处理 if (ref.getId() == -1) { return targetList.getViewList().stream() .filter(e -> e.getId() == -1) .findFirst().orElse(null); } // 根据ID查找实体 return targetList.getViewList().stream() .filter(e -> ref.getId().equals(e.getId())) .findFirst() .orElse(null); } private void associateReferenceList(SynchronizableEntity entity, Field field) throws Exception { Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return; Type itemType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; if (!(itemType instanceof Class) || !SynchronizableEntity.class.isAssignableFrom((Class<?>) itemType)) { return; } List<?> refList = (List<?>) field.get(entity); if (refList == null) { field.set(entity, new ArrayList<>()); return; } List<SynchronizableEntity> resolvedList = new ArrayList<>(); for (Object ref : refList) { if (ref instanceof SynchronizableEntity) { SynchronizableEntity resolved = findTargetEntity( (SynchronizableEntity) ref, (Class<?>) itemType ); if (resolved != null) resolvedList.add(resolved); } } field.set(entity, resolvedList); } } 详细注释
最新发布
06-25
package com.kucun.Service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Service public class DynamicRepositoryService { @Autowired private ApplicationContext applicationContext; // 存储实体名称(小写)到Repository的映射 private final Map<String, JpaRepository<?, ?>> repositoryMap = new HashMap<>(); // 存储实体类到Repository的映射 private final Map<Class<?>, JpaRepository<?, ?>> classRepositoryMap = new HashMap<>(); public Map<Class<?>, JpaRepository<?, ?>> getClassRepositoryMap() { return classRepositoryMap; } public DynamicRepositoryService() { super(); } /** * 初始化方法:扫描所有JpaRepository并建立映射 */ @PostConstruct public void init() { // 获取所有JpaRepository的实现 Map<String, JpaRepository> repositories = applicationContext.getBeansOfType(JpaRepository.class); repositories.forEach((beanName, repo) -> { // 通过反射获取Repository管理的实体类 Class<?> entityClass = resolveEntityClass(repo); if (entityClass != null) { // 建立映射:实体类名(小写)-> Repository repositoryMap.put(entityClass.getSimpleName().toLowerCase(), repo); // 建立映射:实体类-> Repository classRepositoryMap.put(entityClass, repo); } }); } /** * 解析Repository管理的实体类 * @param repository JpaRepository实例 * @return 管理的实体类 */ private Class<?> resolveEntityClass(JpaRepository<?, ?> repository) { // 获取Repository接口的参数 return Arrays.stream(repository.getClass().getGenericInterfaces()) .filter(genericInterface -> genericInterface.getTypeName().contains("JpaRepository")) .findFirst() .map(ParameterizedType.class::cast) .map(type -> (Class<?>) type.getActualTypeArguments()[0]) .orElse(null); } /** * 根据实体名称获取Repository * @param entityName 实体名称(小写) * @return 对应的JpaRepository */ public JpaRepository<?, ?> getJpaRepository(String entityName) { JpaRepository<?, ?> repo = repositoryMap.get(entityName.toLowerCase()); if (repo == null) { throw new IllegalArgumentException("未找到实体 " + entityName + " 对应的Repository"); } return repo; } /** * 根据实体类获取Repository * @param entityClass 实体类 * @return 对应的JpaRepository */ public JpaRepository<?, ?> getJpaRepository(Class<?> entityClass) { JpaRepository<?, ?> repo = classRepositoryMap.get(entityClass); if (repo == null) { throw new IllegalArgumentException("未找到实体类 " + entityClass.getName() + " 对应的Repository"); } return repo; } /** * 获取所有注册的Repository映射 * @return 实体名称到Repository的映射 */ public Map<String, JpaRepository<?, ?>> getRepositoryNameMap() { return repositoryMap; } } 运行时两个集合都是空的
06-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值