Gson解析泛型对象时TypeToken的使用方法

本文深入探讨了在Android开发中使用Gson进行JSON解析时遇到的泛型类型擦除问题,并提供了使用TypeToken的解决方案,确保泛型参数的正确反序列化。

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

在android开发中经常需要从接口服务器获取数据,然后展示在手机界面上。其中手机端和接口服务器之间通常使用json数据来进行通信。

常用的解析场景如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{\n" +
                "    \"name\": \"BeJson\"}";
        Gson gson = new Gson();
        DataBean bean = gson.fromJson(jsonData, DataBean.class);
        Log.e("MainActivity", "bean name: " + bean.name);
        Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean));
    }

    class DataBean{
        public String name;
    }
}

但是上述代码在遇到泛型时就会遇到问题。示例如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{\n" +
                "    \"name\": \"BeJson\"}";
        Gson gson = new Gson();
        DataBean bean = gson.fromJson(jsonData, DataBean.class);
        Log.e("MainActivity", "bean name: " + bean.name);
        Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean));

        Foo<DataBean> foo = new Foo<DataBean>();
        foo.value = bean;
        //测试中使用gson 2.2.4版本,序列化正常
        Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo)); 

        String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
        Foo<DataBean> genericBean = gson.fromJson(genericData, foo.getClass());
       //反序列化失败,genericBean.value的类型被反序列化为com.google.gson.internal.LinkedTreeMap。运行时报错
        Log.e("MainActivity", "generic bean value : " + genericBean.value.toString());
    }

    class DataBean{
        public  String name;
    }

    class Foo<T> {
        T value;
    }
}

导致上述问题的原因是Java在运行时,泛型参数的类型会被擦除,导致在运行期间所有的泛型类型都是Object类型。

泛型运行时类型擦除

泛型的运行时类型擦除问题,可以用以下例子帮助理解。

//代码
ArrayList<String> stringList = Lists.newArrayList();
ArrayList<Integer> intList = Lists.newArrayList();
System.out.println("intList type is " + intList.getClass());
System.out.println("stringList type is " + stringList.getClass());
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));

//运行结果
intList type is class java.util.ArrayList
stringList type is class java.util.ArrayList
true

上述代码中两个泛型类型的ArrayList,一个参数是String,另一个是Integer,当输出getClass方法的类型时均为java.util.ArrayList。而泛型类型擦除导致第三行输出为true,即运行时认为stringList和intList类型一致。

TypeToken类是用来解决java运行时泛型类型被擦除的问题的。通过TypeToken可以获得具体的参数类型。下例中的输出结果即可看出。

TypeToken<ArrayList<String>> typeToken = new TypeToken<ArrayList<String>>() {};
System.out.println(typeToken.getType());

//运行结果
java.util.ArrayList<java.lang.String>

关于TypeToken的具体原理可阅读参考文档Android:Gson通过借助TypeToken获取泛型参数的类型的方法

解决方案

针对上述运行出错的代码,可以通过使用TypeToken来修正问题。示例如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String jsonData = "{\n" +
                "    \"name\": \"BeJson\"}";
        Gson gson = new Gson();
        DataBean bean = gson.fromJson(jsonData, DataBean.class);
        Log.e("MainActivity", "bean name: " + bean.name);
        Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean));

        Foo<DataBean> foo = new Foo<DataBean>();
        foo.value = bean;
        Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo));

        String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
        TypeToken<Foo<DataBean>> typeToken = new TypeToken<Foo<DataBean>>(){};
        Foo<DataBean> genericBean = gson.fromJson(genericData, typeToken.getType());
        Log.e("MainActivity", "generic bean value : " + gson.toJson(genericBean.value));
    }

    class DataBean{
        public  String name;
    }

    class Foo<T> {
        T value;
    }
}

参考文档:
guava反射TypeToken解决泛型运行时类型擦除的问题
Android:Gson通过借助TypeToken获取泛型参数的类型的方法
在Android Studio中如何添加GSON 并使用GsonFormat快速实现实体类

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值