Java Gson 源码分析:TypeToken


#Java Gson#


如何使用

示例1

package org.example;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.Data;
import org.junit.jupiter.api.Test;

import java.util.List;

public class TestTypeToken {

    @Data
    public static class UserInfo {
        private String name;
    }

    @Data
    public static class DataHolder<T> {
        private T data;
    }

    @Test
    public void test_01() {
        TypeToken typeToken = new TypeToken<Long>(){};

        System.out.println("getType: " + typeToken.getType());
        // 以上代码输出: getType: class java.lang.Long
        System.out.println("getRawType: " + typeToken.getRawType());
        // 以上代码输出: getRawType: class java.lang.Long
    }


    @Test
    public void test_02() {
        TypeToken typeToken = new TypeToken<List<Long>>(){};

        System.out.println("getType: " + typeToken.getType());
        // 以上代码输出: getType: java.util.List<java.lang.Long>
        System.out.println("getRawType: " + typeToken.getRawType());
        // 以上代码输出: getRawType: interface java.util.List
    }

    @Test
    public void test_03() {
        TypeToken typeToken = new TypeToken<List<Long>>(){};
        Gson gson = new Gson();
        List<Long> list = gson.fromJson("[1,2,34]", typeToken.getType());
        System.out.println("list: " + list);
        // 以上代码输出: list: [1, 2, 34]
    }

    @Test
    public void test_04() {
        TypeToken typeToken = new TypeToken<DataHolder<Long>>(){};

        System.out.println("getType: " + typeToken.getType());
        // 以上代码输出: getType: org.example.TestTypeToken$DataHolder<java.lang.Long>
        System.out.println("getRawType: " + typeToken.getRawType());
        // 以上代码输出: getRawType: class org.example.TestTypeToken$DataHolder

        Gson gson = new Gson();
        DataHolder<Long> result = gson.fromJson("{\"data\": 123}", typeToken.getType());
        System.out.println("result: " + result);
        // 以上代码输出: result: TestTypeToken.DataHolder(data=123)
    }


}

源码分析: 如何获取泛型变量的实际类型

这里不分析 TypeToken 的代码,我们自己写个实现。原理是类似的。

示例1

package org.example;

import lombok.Data;
import org.junit.jupiter.api.Test;

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

public class TestTypeToken02 {

    @Data
    public static class DataHolder<T> {
        private T data;
    }

    public static class MyTypeToken<T> {
    }

    @Test
    public void test() throws NoSuchFieldException {

        // 生成 MyTypeToken 的匿名子类
        MyTypeToken myTypeToken = new MyTypeToken<DataHolder<String>>(){};

        // 获取 myTypeToken 父类
        Type type = myTypeToken.getClass().getGenericSuperclass();
        // 下面的代码输出: org.example.TestTypeToken02$MyTypeToken<org.example.TestTypeToken02$DataHolder<java.lang.String>>
        System.out.println(type);
        assert type instanceof ParameterizedType;
        ParameterizedType parameterizedType = (ParameterizedType) type;
        assert parameterizedType.getActualTypeArguments().length == 1;

        // 获取 MyTypeToken<DataHolder<String>> 中的 DataHolder<String> 类型信息
        Type dataHolderType = parameterizedType.getActualTypeArguments()[0];
        assert dataHolderType instanceof ParameterizedType;
        ParameterizedType dataHolderParameterizedType = (ParameterizedType) dataHolderType;
        // 以下代码输出: org.example.TestTypeToken02$DataHolder<java.lang.String>
        System.out.println(dataHolderParameterizedType);
        assert dataHolderParameterizedType.getRawType() == DataHolder.class;

        // 获取 data 字段
        Field dataField = ((Class)dataHolderParameterizedType.getRawType()).getDeclaredField("data");
        Type dataFieldGenericType = dataField.getGenericType();
        assert dataFieldGenericType instanceof TypeVariable;

        // 获取 data 字段的泛型类型,在类的泛型参数列表中的位置
        TypeVariable dataFieldTypeVariable = (TypeVariable) dataFieldGenericType;
        TypeVariable[] typeVariableArray = ((Class<?>) dataHolderParameterizedType.getRawType()).getTypeParameters();

        int index = -1;
        for (int i = 0; i < typeVariableArray.length; i++) {
            if (dataFieldTypeVariable == typeVariableArray[i]) {
                index = i;
            }
        }
        assert index != -1;

        // 找到泛型变量的真实类型
        Type dataFieldRealType = dataHolderParameterizedType.getActualTypeArguments()[index];
        assert dataFieldRealType == String.class;

        // 下面的代码输出: data 字段的真实类型是: class java.lang.String
        System.out.println("data 字段的真实类型是: " + dataFieldRealType);

    }

}

示例2

package org.example;

import lombok.Data;
import org.junit.jupiter.api.Test;

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

public class TestTypeToken02 {

    @Data
    public static class DataHolder<T, S> {
        private T data01;
        private S data02;
        private T data03;
    }

    public static class MyTypeToken<T> {
    }

    @Test
    public void test() throws NoSuchFieldException {

        // 生成 MyTypeToken 的匿名子类
        MyTypeToken myTypeToken = new MyTypeToken<DataHolder<String, Integer>>(){};

        // 获取 myTypeToken 父类
        Type type = myTypeToken.getClass().getGenericSuperclass();
        assert type instanceof ParameterizedType;
        ParameterizedType parameterizedType = (ParameterizedType) type;
        assert parameterizedType.getActualTypeArguments().length == 1;

        // 获取 MyTypeToken<DataHolder<String>> 中的 DataHolder<String> 类型信息
        Type dataHolderType = parameterizedType.getActualTypeArguments()[0];
        assert dataHolderType instanceof ParameterizedType;
        ParameterizedType dataHolderParameterizedType = (ParameterizedType) dataHolderType;
        assert dataHolderParameterizedType.getRawType() == DataHolder.class;

        // 便利字段

        for (Field field : ((Class)dataHolderParameterizedType.getRawType()).getDeclaredFields()) {

            // 获取 data 字段
            Type fieldGenericType = field.getGenericType();
            assert fieldGenericType instanceof TypeVariable;

            // 获取 data 字段的泛型类型,在类的泛型参数列表中的位置
            TypeVariable fieldTypeVariable = (TypeVariable) fieldGenericType;
            TypeVariable[] typeVariableArray = ((Class<?>) dataHolderParameterizedType.getRawType()).getTypeParameters();

            int index = -1;
            for (int i = 0; i < typeVariableArray.length; i++) {
                if (fieldTypeVariable == typeVariableArray[i]) {
                    index = i;
                }
            }
            assert index != -1;

            // 找到泛型变量的真实类型
            Type dataFieldRealType = dataHolderParameterizedType.getActualTypeArguments()[index];

            // 下面的代码输出: data 字段的真实类型是: class java.lang.String
            System.out.printf("字段名: %s, 泛型类型: %s, 真实类型: %s%n", field.getName(), fieldTypeVariable, dataFieldRealType);
        }

    }

}

执行结果:

字段名: data01, 泛型类型: T, 真实类型: class java.lang.String
字段名: data02, 泛型类型: S, 真实类型: class java.lang.Integer
字段名: data03, 泛型类型: T, 真实类型: class java.lang.String

示例3

package org.example;

import lombok.Data;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;

public class TestTypeToken02 {

    @Data
    public static class DataHolder<T> {
        private List<String> data01;
        private List<T> data02;
    }

    public static class MyTypeToken<T> {
    }

    @Test
    public void test() throws NoSuchFieldException {

        // 生成 MyTypeToken 的匿名子类
        MyTypeToken myTypeToken = new MyTypeToken<DataHolder<String>>(){};

        // 获取 myTypeToken 父类
        Type type = myTypeToken.getClass().getGenericSuperclass();
        assert type instanceof ParameterizedType;
        ParameterizedType parameterizedType = (ParameterizedType) type;
        assert parameterizedType.getActualTypeArguments().length == 1;

        // 获取 MyTypeToken<DataHolder<String>> 中的 DataHolder<String> 类型信息
        Type dataHolderType = parameterizedType.getActualTypeArguments()[0];
        assert dataHolderType instanceof ParameterizedType;
        ParameterizedType dataHolderParameterizedType = (ParameterizedType) dataHolderType;
        assert dataHolderParameterizedType.getRawType() == DataHolder.class;

        // 遍历字段
        for (Field field : ((Class)dataHolderParameterizedType.getRawType()).getDeclaredFields()) {
            System.out.println("---- 处理字段: " + field.getName());
            Type fieldGenericType = field.getGenericType();
            System.out.println(fieldGenericType);
            if (fieldGenericType instanceof ParameterizedType) {
                System.out.println("该字段是 ParameterizedType,下面尝试获取该字段的类型和类型参数的实际类型");
                ParameterizedType fieldParameterizedType = (ParameterizedType) fieldGenericType;
                System.out.println("rawType: " + fieldParameterizedType.getRawType());
                System.out.println("开始遍历类型参数");
                for (Type typeArg : fieldParameterizedType.getActualTypeArguments()) {
                    if (typeArg instanceof Class) {
                        System.out.println("类型参数: " + typeArg);
                    }
                    else if (typeArg instanceof TypeVariable) {
                        TypeVariable typeArgTypeVariable = (TypeVariable) typeArg;
                        TypeVariable[] typeVariableArray = ((Class<?>) dataHolderParameterizedType.getRawType()).getTypeParameters();

                        int index = -1;
                        for (int i = 0; i < typeVariableArray.length; i++) {
                            if (typeArgTypeVariable == typeVariableArray[i]) {
                                index = i;
                            }
                        }
                        assert index != -1;

                        // 找到泛型变量的真实类型
                        Type typeArgRealType = dataHolderParameterizedType.getActualTypeArguments()[index];
                        System.out.printf("类型参数: %s, 实际类型: %s\n", typeArg, typeArgRealType);
                    }
                    else {
                        System.out.println("其他情况: " + typeArg);
                    }

                }

            }
        }

    }

}

执行结果:

---- 处理字段: data01
java.util.List<java.lang.String>
该字段是 ParameterizedType,下面尝试获取该字段的类型和类型参数的实际类型
rawType: interface java.util.List
开始遍历类型参数
类型参数: class java.lang.String
---- 处理字段: data02
java.util.List<T>
该字段是 ParameterizedType,下面尝试获取该字段的类型和类型参数的实际类型
rawType: interface java.util.List
开始遍历类型参数
类型参数: T, 实际类型: class java.lang.String

( 本文完 )