サーバーに接続し、いくつかの引数に基づいて、GSONで(ジェネリックを介して)指定されたクラスに解析されるjson-stringを取得するクラスを作成しています。
担当クラスのストリップバージョンは次のようになります。
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; }
}
(- JSON
変数は次のようになります。)
逆シリアル化されたデータを格納するクラスは次のようになります。
class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
データが逆シリアル化されるクラス:
public class Language {
public String alias;
public String label;
}
そして実行するコードは上記のクラスを利用します:
Executor<Language> executor = new Executor<Language();
List<Language> languages = executor.execute().getResponse().getData();
System.out.println(languages.get(0).alias); // exception occurs here
次の例外が発生します
ClassCastException:com.google.gson.internal.StringMapをsunnerberg.skolbibliotek.book.Languageにキャストできません
ヘルプや提案は大歓迎です!
簡単に言えば、TypeToken
outの作成を移動し、トークン()を作成するときExecutor
にT
inをバインドして、型トークンをコンストラクターに渡す必要があるということです。Response<T>
new TypeToken<Response<Language>>() {}
Executor
長い答えは:
タイプのジェネリックは、ジェネリックパラメーターがバインドされてコンパイルされた場合を除き、通常は実行時に消去されます。その場合、コンパイラーは総称型情報をコンパイル済みクラスに挿入します。それ以外の場合、それは不可能です。
たとえば、次のことを考慮してください。
List<Integer> foo = new ArrayList<Integer>();
class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();
実行時に型がコンパイル時にバインドされるため、Javaには整数が bar
含まれていることがわかり、ジェネリック型情報はクラスファイルに保存されます。ただし、のジェネリック型情報は消去されるため、実行時にが含まれていると判断することは実際には不可能です。Integer
ArrayList
IntegerList
foo
foo
Integer
そのため、GSONでJSONデータを解析する場合のように、実行前に通常は消去される状況で、ジェネリック型情報が必要になることがよくあります。これらの状況では、型トークンIntegerList
を使用することにより、コンパイル時に型情報が(上記の例のように)バインドされたときに保持されるという事実を利用できます。
今あなたのコードに:
Type responseType = new TypeToken<Response<T>>() {}.getType();
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"
ちなみに、私は私のマシンで上記をテストしました。
長すぎると申し訳ありません!
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加