如何在运行时(Runtime)获得泛型的真正类型

前言

由于Java 的类型擦除机制,在编译时泛型都被转为了Object,例如List<String>经过编译之后将变为类型 List。可以通过以下的方式再运行时获得泛型的真正类型

泛型如何获得具体类型

List 例子如下

来自:https://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list

package test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class java.lang.Integer.
    }
}

Map 的例子如下

来自:https://stackoverflow.com/questions/3687766/how-to-get-value-type-of-a-map-in-java

import java.lang.reflect.*;
import java.util.*;

public class Generic {
    private Map<String, Number> map = new HashMap<String, Number>();

    public static void main(String[] args) {
        try {
            ParameterizedType pt = (ParameterizedType)Generic.class.getDeclaredField("map").getGenericType();
            for(Type type : pt.getActualTypeArguments()) {
                System.out.println(type.toString());
            }
        } catch(NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

实际二者都利用的反射,都是基于 java.lang.reflect.ParameterizedType

jackson 中如何反序列化泛型

jackson 中将JSON 转为Map 的可以通过如下代码实现,方式一:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"mkyong\", \"age\":29}";

Map map = mapper.readValue(json, Map.class);
Object name = map.get("name")

上述只是指定了是 Map 类型,但是没有指定Map里边存放的数据是什么类型,所以得到结果之后还需要对 Object name 做一次强制类型转换才能够使用。

可以使用方式二,告知实际 Map 中存放的对象,从而得到正确的类型,代码如下所示:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"mkyong\", \"age\":29}";

Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});

TypeReference实际上就是告诉了 ObjectMapper 反序列化时要转换的真正类型是什么。

TypeReference 源码

package com.fasterxml.jackson.core.type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public abstract class TypeReference<T> implements Comparable<TypeReference<T>> {
    protected final Type _type;

    protected TypeReference() {
        Type superClass = this.getClass().getGenericSuperclass();
        if (superClass instanceof Class) {
            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
        } else {
            this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
        }
    }

    public Type getType() {
        return this._type;
    }

    public int compareTo(TypeReference<T> o) {
        return 0;
    }
}

有一个 protected 的构造器,所以在使用的时候默认就会执行该构造器,上述方案二将会走到分支代码 this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];,从而 getType 能够得到正确的类型。实际上也是根据 ParameterizedType 获得真正的类型。

通过 TypeReference 获得真正类型

代码类似如下,最后得到的 tmpType1Class 类型,就能够基于它其他的操作了。

TypeReference<Map<String, Test>> typeReference = new TypeReference<Map<String, Test>>(){};
ParameterizedType type = (ParameterizedType)typeReference.getType();
for (Type tmpType : type.getActualTypeArguments()) {
    Class<?> tmpType1 = (Class<?>) tmpType;
    System.out.println(tmpType1);
}

欢迎转载,但请注明本文链接,谢谢你。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android相关

AndroidStudio出现UnsupportedClassVersionError

Android Studio升级到2.0之后,Build Tool也随着更新了,然后运行了原来的工程后,突然出现了,如下异常

13220
来自专栏Android相关

Gradle For Android(1)--使用AndroidStudio开始Gradle

build.gradle是为了构建Android项目的脚本。Gradle构建脚本并不是使用传统的XML,而是基于Groovy的domain-specific l...

12630
来自专栏Android相关

Java线程池---ThreadPoolExecutor中的ctl变量

ThreadPoolExecutor中有一个控制状态的属性叫ctl,它是一个AtomicInteger类型的变量,它包含两个概念:

21940
来自专栏Android相关

Gradle For Android(7)--创建Task以及Plugin

到目前为止,我们已经看到了很多Gradle构建的属性,并且知道了怎么去执行Tasks。这一章,会更多的了解这些属性,并且创建我们自己的Task。一旦知道如何自定...

24020
来自专栏Android相关

Gradle For Android(5)--管理多Module的编译

通常一个多Module的工程会有一个根目录,而它的子目录下包含了所有的Module。为了告诉Gradle这个Project的结构,这个目录下包含了所有要构建的M...

37730
来自专栏Android相关

Java多线程---ReentrantLock

重入锁。同一个线程可以锁住多次,但是在使用完毕后,必须释放多次锁才能将锁完全释放,否则,还是该线程还是会处于阻塞状态。并且重入锁有两种,一种是公平锁,一种是非公...

17640
来自专栏Android相关

Gradle解决多lib冲突

在引入第三方jar包的时候,可能会遇到很多库的冲突,例如均使用了OkHttp,RxJava等等,在打包的时候就会遇到Class多个定义的问题。而目前Androi...

35820
来自专栏Android相关

Java线程池---ThreadPoolExecutor解析

在ThreadPoolExecutor中的ctl变量中已经解释了线程池中ctl变量中,高3位代表线程池当前的状态,而低28位表示线程池中线程的总数。

17130
来自专栏Android相关

Android7.0中的ResourceNotFoundException

随着Android N的出现,适配7.0的问题也成为了各大产品头疼的问题。而最近在我们的平台上面收到了7.0的Crash。具体的栈如下:

40050
来自专栏Android相关

Java线程池---ShutDown以及ShutDownNow解析

初始化一个有序的关闭,之前提交的任务都会被执行,但是新提交的任务则不会被允许放入任务队列中。如果之前被调用过了的话,那么再次调用也没什么用。这个方法不会等待之前...

14120

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励