Zar*_*Zar 10 java oop generics reflection gson
我正在编写一个将连接到服务器并基于某些参数的类,检索一个json-string,该字符串将使用GSON解析为指定的(通过泛型)类.
这个课程的精简版看起来像这样:
class Executor<T> {
private Response<T> response;
public void execute() {
Type responseType = new TypeToken<Response<T>>() {}.getType();
this.response = new Gson().fromJson(json, responseType);
}
public Response<T> getResponse() { return this.response; }
}
Run Code Online (Sandbox Code Playgroud)
(JSON变量看起来像这样.)
一旦反序列化存储数据的类如下所示:
class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
Run Code Online (Sandbox Code Playgroud)
数据尝试反序列化的类:
public class Language {
public String alias;
public String label;
}
Run Code Online (Sandbox Code Playgroud)
运行的代码使用上面的类:
Executor<Language> executor = new Executor<Language();
List<Language> languages = executor.execute().getResponse().getData();
System.out.println(languages.get(0).alias); // exception occurs here
Run Code Online (Sandbox Code Playgroud)
这导致以下异常
ClassCastException:com.google.gson.internal.StringMap无法强制转换为sunnerberg.skolbibliotek.book.Language
任何帮助或建议都非常感谢!
mat*_*tts 29
简短的回答是你需要移动创建TypeTokenout Executor,T在Response<T>创建token(new TypeToken<Response<Language>>() {})时绑定in ,并将类型标记传递给Executor构造函数.
答案很长的答案是:
类型上的泛型通常在运行时被擦除,除非使用泛型参数绑定编译类型.在这种情况下,编译器将泛型类型信息插入到已编译的类中.在其他情况下,这是不可能的.
例如,考虑一下:
List<Integer> foo = new ArrayList<Integer>();
class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();
Run Code Online (Sandbox Code Playgroud)
在运行时,Java 知道 bar包含整数,因为Integer类型ArrayList在编译时被绑定,因此泛型类型信息保存在IntegerList类文件中.但是,foo擦除了泛型类型信息,因此在运行时无法确定foo应该包含Integers.
因此,经常会出现在通常在运行时之前擦除通用类型信息的情况,例如在GSON中解析JSON数据的情况.在这些情况下,我们可以利用类型信息在编译时被绑定的事实(如上IntegerList例所示)通过使用类型标记来保留,这类型标记实际上只是很小的匿名类,可以方便地存储泛型类型信息.
现在到你的代码:
Type responseType = new TypeToken<Response<T>>() {}.getType();
Run Code Online (Sandbox Code Playgroud)
在这个Executor类的这一行中,我们创建了一个匿名类(继承自TypeToken),它Response<T>在编译时具有硬编码(绑定)类型.所以在运行时,GSON能够确定你想要一个对象Response<T>.但它不知道是什么T,因为你没有在编译时指定它!因此,GSON无法确定它所创建List的Response对象中的类型,而只是创建了一个类型StringMap.
这个故事的寓意是你需要T在编译时指定它.如果Executor要一般使用,则可能需要在客户端代码中在该类之外创建类型标记.就像是:
class Executor<T> {
private TypeToken<Response<T>> responseType;
private Response<T> response;
public Executor(TypeToken<Response<T>> responseType) {
this.responseType = responseType;
}
public void execute() {
this.response = new Gson().fromJson(json, responseType.getType());
}
public Response<T> getResponse() { return this.response; }
}
// client code:
Executor<Language> executor = new Executor<Language>(new TypeToken<Response<Language>>() { });
executor.execute();
List<Language> languages = executor.getResponse().getData();
System.out.println(languages.get(0).alias); // prints "be"
Run Code Online (Sandbox Code Playgroud)
顺便说一下,我在我的机器上测试了上面的内容.
对不起,如果太长了!