在android开发中经常需要从接口服务器获取数据,然后展示在手机界面上。其中手机端和接口服务器之间通常使用json数据来进行通信。
常用的解析场景如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
setContentView(.activity_main);
String jsonData = "{\n" +
" \"name\": \"BeJson\"}";
Gson gson = new Gson();
DataBean bean = (jsonData, );
("MainActivity", "bean name: " + );
("MainActivity", "bean jsonStr: " + (bean));
}
class DataBean{
public String name;
}
}
但是上述代码在遇到泛型时就会遇到问题。示例如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
setContentView(.activity_main);
String jsonData = "{\n" +
" \"name\": \"BeJson\"}";
Gson gson = new Gson();
DataBean bean = (jsonData, );
("MainActivity", "bean name: " + );
("MainActivity", "bean jsonStr: " + (bean));
Foo<DataBean> foo = new Foo<DataBean>();
= bean;
//测试中使用gson 2.2.4版本,序列化正常
("MainActivity", "foo jsonStr: " + (foo));
String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
Foo<DataBean> genericBean = (genericData, ());
//反序列化失败,的类型被反序列化为。运行时报错
("MainActivity", "generic bean value : " + ());
}
class DataBean{
public String name;
}
class Foo<T> {
T value;
}
}
导致上述问题的原因是Java在运行时,泛型参数的类型会被擦除,导致在运行期间所有的泛型类型都是Object类型。
泛型运行时类型擦除
泛型的运行时类型擦除问题,可以用以下例子帮助理解。
//代码
ArrayList<String> stringList = ();
ArrayList<Integer> intList = ();
("intList type is " + ());
("stringList type is " + ());
(().isAssignableFrom(()));
//运行结果
intList type is class
stringList type is class
true
上述代码中两个泛型类型的ArrayList,一个参数是String,另一个是Integer,当输出getClass方法的类型时均为。而泛型类型擦除导致第三行输出为true,即运行时认为stringList和intList类型一致。
TypeToken类是用来解决java运行时泛型类型被擦除的问题的。通过TypeToken可以获得具体的参数类型。下例中的输出结果即可看出。
TypeToken<ArrayList<String>> typeToken = new TypeToken<ArrayList<String>>() {};
(());
//运行结果
<>
关于TypeToken的具体原理可阅读参考文档Android:Gson通过借助TypeToken获取泛型参数的类型的方法。
解决方案
针对上述运行出错的代码,可以通过使用TypeToken来修正问题。示例如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
setContentView(.activity_main);
String jsonData = "{\n" +
" \"name\": \"BeJson\"}";
Gson gson = new Gson();
DataBean bean = (jsonData, );
("MainActivity", "bean name: " + );
("MainActivity", "bean jsonStr: " + (bean));
Foo<DataBean> foo = new Foo<DataBean>();
= bean;
("MainActivity", "foo jsonStr: " + (foo));
String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
TypeToken<Foo<DataBean>> typeToken = new TypeToken<Foo<DataBean>>(){};
Foo<DataBean> genericBean = (genericData, ());
("MainActivity", "generic bean value : " + ());
}
class DataBean{
public String name;
}
class Foo<T> {
T value;
}
}
参考文档:
guava反射TypeToken解决泛型运行时类型擦除的问题
Android:Gson通过借助TypeToken获取泛型参数的类型的方法
在Android Studio中如何添加GSON 并使用GsonFormat快速实现实体类