编程题(解读Gson库中的方法)

一个编程题引发的对泛型的特性了解

某金融系统的某报表查询页面,显示了如下的表格:

营业部 客户号 客户姓名 资产账户 预约人 预调整日期 操作
21营业部 210011210214001 产品客户信息 210011210214201 admin 20220621 查看删除
21营业部 210011210213001 机构客户号 210011210213301 admin 20220621 查看删除
21营业部 210011210213001 机构客户号 210011210213201 admin 20220621 查看删除
21营业部 210011210212001 个人客户号gs 210011210212301 admin 20220621 查看删除
21营业部 210011210212001 个人客户号gs 210011210212101 admin 20220621 查看删除

客户想对这个表格的数据进行校验,所以前端同学调用了一个组件,将表格的图片识别成了某种格式,计划将数据传递给后端进行验证,传递的内容如下:
[{“head”:“资产账户”,“col”:“4”,“text”:" 210011210214201 “,“row”:“1”},{“head”:“客户姓名”,“col”:“3”,“text”:” 产品客户信息 “,“row”:“1”},{“head”:“客户号”,“col”:“2”,“text”:” 210011210214001 “,“row”:“1”},{“head”:“营业部”,“col”:“1”,“text”:” 21营业部 “,“row”:“1”},{“head”:“操作”,“col”:“7”,“text”:” 查看删除",“row”:“1”},{“head”:“预调整日期”,“col”:“6”,“text”:" 20220621 “,“row”:“1”},{“head”:“预约人”,“col”:“5”,“text”:” admin “,“row”:“1”},{“head”:“资产账户”,“col”:“4”,“text”:” 210011210212101 “,“row”:“5”},{“head”:“客户姓名”,“col”:“3”,“text”:” 个人客户号gs “,“row”:“5”},{“head”:“客户号”,“col”:“2”,“text”:” 210011210212001 “,“row”:“5”},{“head”:“营业部”,“col”:“1”,“text”:” 21营业部 “,“row”:“5”},{“head”:“营业部”,“col”:“1”,“text”:” 21营业部 “,“row”:“2”},{“head”:“操作”,“col”:“7”,“text”:” 查看删除",“row”:“5”},{“head”:“客户姓名”,“col”:“3”,“text”:" 机构客户号 “,“row”:“2”},{“head”:“预调整日期”,“col”:“6”,“text”:” 20220621 “,“row”:“5”},{“head”:“客户号”,“col”:“2”,“text”:” 210011210213001 “,“row”:“2”},{“head”:“预约人”,“col”:“5”,“text”:” admin “,“row”:“5”},{“head”:“预约人”,“col”:“5”,“text”:” admin “,“row”:“2”},{“head”:“资产账户”,“col”:“4”,“text”:” 210011210213301 “,“row”:“2”},{“head”:“操作”,“col”:“7”,“text”:” 查看删除",“row”:“2”},{“head”:“预调整日期”,“col”:“6”,“text”:" 20220621 “,“row”:“2”},{“head”:“操作”,“col”:“7”,“text”:” 查看删除",“row”:“4”},{“head”:“预调整日期”,“col”:“6”,“text”:" 20220621 “,“row”:“4”},{“head”:“预调整日期”,“col”:“6”,“text”:” 20220621 “,“row”:“3”},{“head”:“预约人”,“col”:“5”,“text”:” admin “,“row”:“3”},{“head”:“资产账户”,“col”:“4”,“text”:” 210011210213201 “,“row”:“3”},{“head”:“客户姓名”,“col”:“3”,“text”:” 机构客户号 “,“row”:“3”},{“head”:“客户号”,“col”:“2”,“text”:” 210011210213001 “,“row”:“3”},{“head”:“营业部”,“col”:“1”,“text”:” 21营业部 “,“row”:“3”},{“head”:“操作”,“col”:“7”,“text”:” 查看删除",“row”:“3”},{“head”:“客户姓名”,“col”:“3”,“text”:" 个人客户 号gs “,“row”:“4”},{“head”:“客户号”,“col”:“2”,“text”:” 210011210212001 “,“row”:“4”},{“head”:“预约人”,“col”:“5”,“text”:” admin “,“row”:“4”},{“head”:“资产账户”,“col”:“4”,“text”:” 210011210212301 “,“row”:“4”},{“head”:“营业部”,“col”:“1”,“text”:” 21营业部 ",“row”:“4”}]

数据说明:

head为列头,text为表格显示文本,row为行编号(从1开始),col为列编号(从1开始)
后端校验的规则为:给出某几列和其中的值,查询是否在表格中,并且在同一行。
后端同学接到前端发来的数据后,表示这个数据看着头大,想对数据重新整理。他计划使用Map对象将表格的表头和内容整理成键值对的形式,key为列表头,value为表格中的文本。每一行的Map使用List按顺序进行组装,这样显得清晰而且便于后续的表格校验操作。整理后的样式类似于(可不考虑列的顺序,但行的排序需与原表格一致),为了实现通用的前端数据到后端结构的转换程序,我们可以编写一个可以处理任意此类表格数据解析的Java程序。以下是实现该功能的完整代码:


import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.*;

public class TableDataConverter {

    public static void main(String[] args) {
        // 前端传来的JSON数据
        String jsonData = "[{\"head\":\"资产账户\",\"col\":\"4\",\"text\":\" 210011210214201 \",\"row\":\"1\"},"
                        + "{\"head\":\"客户姓名\",\"col\":\"3\",\"text\":\" 产品客户信息 \",\"row\":\"1\"},"
                        + "{\"head\":\"客户号\",\"col\":\"2\",\"text\":\" 210011210214001 \",\"row\":\"1\"},"
                        + "{\"head\":\"营业部\",\"col\":\"1\",\"text\":\" 21营业部 \",\"row\":\"1\"},"
                        + "{\"head\":\"操作\",\"col\":\"7\",\"text\":\" 查看删除\",\"row\":\"1\"},"
                        + "{\"head\":\"预调整日期\",\"col\":\"6\",\"text\":\" 20220621 \",\"row\":\"1\"},"
                        + "{\"head\":\"预约人\",\"col\":\"5\",\"text\":\" admin \",\"row\":\"1\"},"
                        + "{\"head\":\"资产账户\",\"col\":\"4\",\"text\":\" 210011210212101 \",\"row\":\"5\"},"
                        + "{\"head\":\"客户姓名\",\"col\":\"3\",\"text\":\" 个人客户号gs \",\"row\":\"5\"},"
                        + "{\"head\":\"客户号\",\"col\":\"2\",\"text\":\" 210011210212001 \",\"row\":\"5\"},"
                        + "{\"head\":\"营业部\",\"col\":\"1\",\"text\":\" 21营业部 \",\"row\":\"5\"},"
                        + "{\"head\":\"营业部\",\"col\":\"1\",\"text\":\" 21营业部 \",\"row\":\"2\"},"
                        + "{\"head\":\"操作\",\"col\":\"7\",\"text\":\" 查看删除\",\"row\":\"5\"},"
                        + "{\"head\":\"客户姓名\",\"col\":\"3\",\"text\":\" 机构客户号 \",\"row\":\"2\"},"
                        + "{\"head\":\"预调整日期\",\"col\":\"6\",\"text\":\" 20220621 \",\"row\":\"5\"},"
                        + "{\"head\":\"客户号\",\"col\":\"2\",\"text\":\" 210011210213001 \",\"row\":\"2\"},"
                        + "{\"head\":\"预约人\",\"col\":\"5\",\"text\":\" admin \",\"row\":\"5\"},"
                        + "{\"head\":\"预约人\",\"col\":\"5\",\"text\":\" admin \",\"row\":\"2\"},"
                        + "{\"head\":\"资产账户\",\"col\":\"4\",\"text\":\" 210011210213301 \",\"row\":\"2\"},"
                        + "{\"head\":\"操作\",\"col\":\"7\",\"text\":\" 查看删除\",\"row\":\"2\"},"
                        + "{\"head\":\"预调整日期\",\"col\":\"6\",\"text\":\" 20220621 \",\"row\":\"2\"},"
                        + "{\"head\":\"操作\",\"col\":\"7\",\"text\":\" 查看删除\",\"row\":\"4\"},"
                        + "{\"head\":\"预调整日期\",\"col\":\"6\",\"text\":\" 20220621 \",\"row\":\"4\"},"
                        + "{\"head\":\"预调整日期\",\"col\":\"6\",\"text\":\" 20220621 \",\"row\":\"3\"},"
                        + "{\"head\":\"预约人\",\"col\":\"5\",\"text\":\" admin \",\"row\":\"3\"},"
                        + "{\"head\":\"资产账户\",\"col\":\"4\",\"text\":\" 210011210213201 \",\"row\":\"3\"},"
                        + "{\"head\":\"客户姓名\",\"col\":\"3\",\"text\":\" 机构客户号 \",\"row\":\"3\"},"
                        + "{\"head\":\"客户号\",\"col\":\"2\",\"text\":\" 210011210213001 \",\"row\":\"3\"},"
                        + "{\"head\":\"营业部\",\"col\":\"1\",\"text\":\" 21营业部 \",\"row\":\"3\"},"
                        + "{\"head\":\"操作\",\"col\":\"7\",\"text\":\" 查看删除\",\"row\":\"3\"},"
                        + "{\"head\":\"客户姓名\",\"col\":\"3\",\"text\":\" 个人客户 号gs \",\"row\":\"4\"},"
                        + "{\"head\":\"客户号\",\"col\":\"2\",\"text\":\" 210011210212001 \",\"row\":\"4\"},"
                        + "{\"head\":\"预约人\",\"col\":\"5\",\"text\":\" admin \",\"row\":\"4\"},"
                        + "{\"head\":\"资产账户\",\"col\":\"4\",\"text\":\" 210011210212301 \",\"row\":\"4\"},"
                        + "{\"head\":\"营业部\",\"col\":\"1\",\"text\":\" 21营业部 \",\"row\":\"4\"}]";

        // 将JSON字符串解析为List<Map<String, String>>结构
        List<Map<String, String>> dataList = parseJsonData(jsonData);

        // 将解析后的数据转换为按行排序的List<Map<String, String>>结构
        List<Map<String, String>> tableData = convertToTableData(dataList);

        // 输出转换后的数据
        for (Map<String, String> row : tableData) {
            System.out.println(row);
        }
    }

    /**
     * 将JSON字符串解析为List<Map<String, String>>结构
     * @param jsonData JSON字符串
     * @return 解析后的List<Map<String, String>>结构
     */
    private static List<Map<String, String>> parseJsonData(String jsonData) {
        Gson gson = new Gson();
        Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
        return gson.fromJson(jsonData, listType);
    }

    /**
     * 将解析后的数据转换为按行排序的List<Map<String, String>>结构
     * @param dataList 解析后的数据列表
     * @return 按行排序的List<Map<String, String>>结构
     */
    private static List<Map<String, String>> convertToTableData(List<Map<String, String>> dataList) {
        Map<Integer, Map<String, String>> rowMap = new HashMap<>();

        // 遍历数据列表,将数据按行整理
        for (Map<String, String> item : dataList) {
            int row = Integer.parseInt(item.get("row").trim());
            String head = item.get("head").trim();
            String text = item.get("text").trim();

            if (!rowMap.containsKey(row)) {
                rowMap.put(row, new HashMap<>());
            }
            rowMap.get(row).put(head, text);
        }

        // 根据行号进行排序,并转换为List<Map<String, String>>结构
        List<Map<String, String>> sortedTableData = new ArrayList<>();
        for (int i = 1; i <= rowMap.size(); i++) {
            if (rowMap.containsKey(i)) {
                sortedTableData.add(rowMap.get(i));
            }
        }

        return sortedTableData;
    }
}

代码分析:

parseJsonData方法:
使用Gson库解析JSON字符串,将其转换为List<Map<String, String>>结构。每个Map表示一个表格单元格的数据。

convertToTableData方法:
使用Map<Integer, Map<String, String>>数据结构,将表格数据按行号整理。Integer为行号,Map<String, String>为该行的数据。
遍历dataList,将每个数据项插入到相应的行号对应的Map中。
最后,将rowMap转换为按行号排序的List<Map<String, String>>结构,以保证输出的数据顺序与表格顺序一致。

引入Gson库的步骤:

  1. 使用 Maven
    如果你的项目使用Maven作为构建工具,可以在pom.xml中添加Gson的依赖。以下是添加Gson依赖的示例:
<!--xml-->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version> <!-- 请根据需要选择最新版本 -->
</dependency>

将上述代码块添加到你的pom.xml文件中的dependencies部分。

示例代码
假设你已经正确添加了Gson依赖,可以使用以下代码来测试Gson的基本功能:


import com.google.gson.Gson;

public class GsonExample {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // 创建一个对象
        Person person = new Person("John", 30);

        // 将对象转换为JSON字符串
        String json = gson.toJson(person);
        System.out.println("JSON: " + json);

        // 将JSON字符串转换回对象
        Person personFromJson = gson.fromJson(json, Person.class);
        System.out.println("Person: " + personFromJson);
    }

    static class Person {
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{name='" + name + "', age=" + age + '}';
        }
    }
}

以上代码会将一个Person对象转换为JSON字符串,并且将JSON字符串转换回Person对象。

选择适合你的项目的方式来引入Gson库,并根据需要进行配置。

这里对listType多说一嘴

listType 是用于将 JSON 数据转换为 Java 对象时的一个类型标识。它用于指定我们希望从 JSON 字符串中解析出什么类型的 Java 对象。在使用 Gson 进行 JSON 解析时,这个概念尤其重要,因为 Gson 需要知道如何将 JSON 数据映射到 Java 对象上。

在 Gson 中,TypeToken 类用于捕获泛型类型信息。因为 Java 在运行时会丢失泛型类型的信息,所以我们需要 TypeToken 来帮助 Gson 知道我们要转换成什么类型。

示例:
将 JSON 转换为 List<Map<String, String>>
假设我们有一个 JSON 字符串,它表示一个包含多个表格行的列表,每行是一个键值对的映射。例如:

//json
[
    {"head": "营业部", "col": "1", "text": "21营业部", "row": "1"},
    {"head": "客户号", "col": "2", "text": "210011210214001", "row": "1"},
    ...
]

我们想将这个 JSON 字符串解析为一个 List<Map<String, String>> 对象,其中每个 Map 表示 JSON 对象中的一个条目。

为了做到这一点,我们需要提供一个 Type 对象,告知 Gson 解析成的具体类型。这里的 Type 对象可以通过 TypeToken 来创建。

//导入所需的类
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

//创建 Gson 实例
Gson gson = new Gson();

//定义要解析的类型
Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
/*这里 TypeToken<List<Map<String, String>>> 是一个匿名子类,它帮助 Gson 识别我们期望的目标类型。在这个例子中,我们期望得到一个 List,其中每个元素都是一个 Map,Map 的键和值都是 String 类型。TypeToken 的 getType 方法返回的是 Type 对象,它是 Gson 在解析 JSON 时需要的类型信息。*/

//解析 JSON 数据
List<Map<String, String>> dataList = gson.fromJson(jsonData, listType);
/*gson.fromJson 方法将 JSON 字符串 jsonData 解析为指定类型的 Java 对象,这里是 List<Map<String, String>>。*/
总结

TypeToken 和 listType 主要用于在运行时保留泛型类型信息,使得 Gson 能够正确地将 JSON 数据映射到 Java 对象。这样可以确保 JSON 数据被正确解析为预期的类型结构,而不会出现类型不匹配的问题。

问题解答

1.TypeToken是怎么实现的

TypeToken 是 Gson 库中的一个辅助类,用于在运行时保持泛型类型的信息。由于 Java 在运行时会丢失泛型的具体类型信息,TypeToken 通过创建匿名子类的方式来捕获这些信息。

TypeToken 的核心思想是利用 Java 的匿名内部类机制来捕获泛型的具体类型。以下是 TypeToken 的基本实现思路:


public class TypeToken<T> {
    private final Type type;

    protected TypeToken() {
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof ParameterizedType) {
            this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
        } else {
            throw new RuntimeException("TypeToken constructed without generic type");
        }
    }

    public Type getType() {
        return type;
    }
}

在构造函数中,通过

getClass().getGenericSuperclass()

  • 获取当前 TypeToken 类的泛型父类信息。这将返回 ParameterizedType 对象,即该对象包含泛型的实际类型参数。
    ParameterizedType 提供 getActualTypeArguments() 方法来获取实际的泛型类型参数。这个方法返回一个 Type 数组,我们可以从中提取出需要的类型。使用匿名内部类。

例如:


Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
/*在这里,TypeToken<List<Map<String, String>>> 是一个匿名内部类,它的泛型参数被传递给 TypeToken 的构造函数,从而使得 type 字段能够正确地保存 List<Map<String, String>> 类型的信息。*/

实例


//getSuperclassTypeParameter 方法:通过获取父类的泛型参数来实现类型捕获。
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Example {
    public static void main(String[] args) {
        Gson gson = new Gson();
        String jsonData = "[{\"head\": \"营业部\", \"col\": \"1\", \"text\": \"21营业部\", \"row\": \"1\"}, ...]";

        Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
        List<Map<String, String>> dataList = gson.fromJson(jsonData, listType);

        // 输出解析后的数据
        for (Map<String, String> row : dataList) {
            System.out.println(row);
        }
    }
}

在这个示例中,TypeToken 帮助 Gson 解析 JSON 字符串,并将其转换为 List<Map<String, String>> 对象,保持了泛型类型信息。

2. superclass 是什么?
superclass 是通过 getClass().getGenericSuperclass() 获取的,它表示当前类的直接父类的类型信息。getGenericSuperclass() 方法返回的是一个 Type 对象,这个 Type 可以是普通的类(即 Class<?>),也可以是参数化类型(即 ParameterizedType),比如 List。

3. superclass instanceof ParameterizedType是什么?
这里使用 instanceof 来判断 superclass 是否是 ParameterizedType 类型。ParameterizedType 是一个接口,表示一个具有实际类型参数的泛型类型。举个例子,如果你有一个类定义如下:


class MyTypeToken extends TypeToken<List<String>> {}

在这个例子中,MyTypeToken 的直接父类是 TypeToken<List>,而 TypeToken<List> 是一个参数化类型,它的泛型参数是 List。因此,对于这个例子,superclass 就是 TypeToken<List>,它是一个 ParameterizedType。

4. ((ParameterizedType) superclass).getActualTypeArguments()
当 superclass 被确认是一个 ParameterizedType 时,我们将其转换为 ParameterizedType 类型,然后调用 getActualTypeArguments() 方法。这个方法返回的是一个 Type 数组,表示泛型类型的实际参数列表。

对于 TypeToken<List> 来说,getActualTypeArguments() 返回的数组中只有一个元素,就是 List。

5. getActualTypeArguments()[0]
由于 TypeToken 只关心它的第一个泛型参数,所以我们取这个数组的第一个元素。这就是泛型类型的实际类型参数,比如 List。

总结

TypeToken 通过利用 Java 的匿名内部类来捕获泛型类型的具体信息,并将这些信息传递给 Gson 库,帮助其在运行时正确解析泛型类型的 JSON 数据。这种方式避免了 Java 在运行时类型擦除的问题,使得 JSON 数据能够被准确地转换为目标类型。
也就是在构造 TypeToken 时,获取它的父类的泛型类型参数。如果父类是一个参数化类型(如 TypeToken<List>),那么这段代码就会提取出 List 这个泛型类型。

举个例子

假设我们有如下的 TypeToken 实例:


TypeToken<List<String>> token = new TypeToken<List<String>>() {};

在这个上下文中,superclass 是 TypeToken<List>,这是一个 ParameterizedType。因此,getActualTypeArguments() 返回 [List],而 getActualTypeArguments()[0] 就是 List。

这个 Type 对象最终会被存储在 TypeToken 类的 type 字段中,用于之后在 Gson 中进行类型转换。

泛型擦除:

问题:那我直接新建一个List<map<string,string>>作为我们gson.fromJson的第二个传参不就可以了吗?

思路是正确的,但直接新建一个 List<Map<String, String>> 作为 gson.fromJson 的第二个参数是不可行的。这是因为 Java 的泛型类型在运行时会被擦除(类型擦除),导致 Gson 无法在运行时识别你希望解析的具体泛型类型。

为什么直接传递 new List<Map<String, String>>() 不行?
Java 的泛型类型在编译时是有类型信息的,但在运行时,Java 使用类型擦除来移除所有的泛型类型信息。也就是说,List<Map<String, String>> 在编译后变成了 List,所以在运行时 Gson 无法知道你希望解析成 List<Map<String, String>>,只能知道你想要解析成 List。

例如

List<Map<String, String>> list = new ArrayList<>();
List<Map<String, String>> result = gson.fromJson(jsonData, list.getClass());

此时 list.getClass() 返回的类型是 ArrayList.class,它并不包含泛型信息,Gson 只会把 JSON 数据解析成一个原生的 List 类型,而不会知道内部的 Map<String, String> 结构。

这就是 TypeToken 存在的原因。TypeToken 通过创建匿名内部类的方式捕获了泛型类型信息,并将其保留到运行时,使得 Gson 在运行时能够知道你希望解析成的目标类型。

使用 TypeToken 通过以下方式将 JSON 解析为 List<Map<String, String>>:


Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
List<Map<String, String>> result = gson.fromJson(jsonData, listType);

listType 变量包含了完整的泛型信息,Gson 能够正确地将 JSON 解析为 List<Map<String, String>>
对象。

总之,因为类型擦除的存在,无法在运行时直接通过实例类型来获得泛型信息,所以必须使用 TypeToken 来保留和传递这些信息给 Gson,从而使其能够正确解析复杂的泛型结构。

TypeToken为什么可以将泛型类型信息保留到运行时

TypeToken 能够将泛型类型信息保留到运行时的关键在于它利用了 Java 的匿名内部类机制,在实例化时捕获了泛型类型的具体信息。下面是详细的解释:

  1. Java 泛型的类型擦除
    在 Java 中,泛型是通过类型擦除来实现的,这意味着泛型类型参数在编译后会被擦除。例如:
List<String> list = new ArrayList<>();

在编译后,List<> 会变成 List,在运行时,类型信息 String 被擦除了。这是为了兼容没有泛型的老版本 Java 代码。

  1. 匿名内部类的作用
    为了在运行时保留泛型的类型信息,TypeToken 通过创建匿名内部类的方式来捕获这些信息。当你创建 TypeToken 的实例时,通常会写成如下形式:

TypeToken<List<String>> typeToken = new TypeToken<List<String>>() {};

这里的 TypeToken<List> 实际上是一个匿名的子类,在创建这个匿名子类的实例时,Java 编译器会在这个匿名子类的字节码中保留 List 的类型信息。具体来说,getClass().getGenericSuperclass() 返回的是这个匿名子类的父类(即 TypeToken<List>),这包含了完整的泛型信息。

  1. ParameterizedType 的使用
    当你调用 getClass().getGenericSuperclass() 时,它返回的是一个 Type 对象,如果这个类有泛型参数,并且是匿名内部类的方式创建的,那么这个 Type 实际上是一个 ParameterizedType,其中包含了泛型参数的实际类型信息。

例如:

Type superclass = getClass().getGenericSuperclass();

在 TypeToken 中,这段代码的作用是获取 TypeToken 的直接父类的类型信息。如果这个父类是一个参数化类型(如 TypeToken<List>),那么 superclass 就是一个 ParameterizedType,它包含了 List 这个泛型参数的信息。

  1. 从 ParameterizedType 中提取泛型信息
    ParameterizedType 接口提供了 getActualTypeArguments() 方法,可以用来获取泛型参数的实际类型。这正是 TypeToken 的核心实现:

Type type = ((ParameterizedType) superclass).getActualTypeArguments()[0];

这行代码从 ParameterizedType 中提取出泛型参数(如 List),并将其保存在 TypeToken 的 type 字段中。

  1. 在运行时使用泛型信息
    一旦 TypeToken 捕获到了泛型类型信息,这些信息就被保留在 TypeToken 实例的 type 字段中,可以在运行时通过 getType() 方法获取这个类型信息,并用于后续的操作,如 JSON 解析:
Type listType = new TypeToken<List<Map<String, String>>>() {}.getType();
List<Map<String, String>> result = gson.fromJson(jsonData, listType);

在这里,Gson 使用 listType 中的泛型信息来正确地解析 JSON 数据。

总结

TypeToken 通过创建匿名内部类的方式,在实例化时捕获了泛型的类型信息。然后,它利用 ParameterizedType 提供的方法,将这些类型信息保留到运行时,使得像 Gson 这样的库可以在运行时正确地解析泛型类型的数据。这种方式有效地克服了 Java 的类型擦除机制,使得泛型类型在运行时仍然可用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值