JSON deserialization with generics

1. 背景

ある日、「退屈の庭」で次のような例に関する疑問が提起されました。

public static void main(String[] args) {

List list = JSONObject.parseObject(jsonString, List.class);

System.out.println(list);

}

この例では fastjson のクラスライブラリを使用しています。

なぜ IntelliJ IDEA で次のような警告が表示されるのでしょうか。また、どのように解決すればよいでしょうか。

抑制アノテーションを使用してこの警告を抑制すればよいと考える人もいるかもしれません。

警告を抑制するだけでよいのでしょうか?

2. 分析

2.1 怪しい挙動には必ず理由がある

IntelliJ IDEA が理由もなく警告メッセージを表示することはありません。警告の理由は前述の通りです。

ジェネリクスなしの List をジェネリクス付きの List に割り当てています。Java コンパイラは、右辺で返されるジェネリクスなしの実際の List が、左辺のジェネリクス付き List の制約に適合しているかどうかを判断できません。

first を third に割り当てる際、first の要素が List の制約、つまりリストのすべての要素が String 型であるという条件を満たしていることを保証できません。

上記のコードを実行しても、エラーは発生しません(笑)。

ただし、foreach ループやイテレータで String 型の要素を取り出してループ処理を行うと、型変換例外が発生します。

型変換例外?

IntelliJ IDEA の jclasslib デコンパイルプラグインを使用して、main メソッドのコードを確認します。

42 行目から 76 行目までの foreach ループのロジックから、内部では List のイテレータを使用して走査し、各要素を取り出して String 型に強制的に変換し、ローカル変数テーブルのインデックス 4 の位置に格納してから出力していることがわかります。

デコンパイルに慣れていない場合は、ターゲットディレクトリに移動し、コンパイル済みのクラスファイルをダブルクリックして、IntelliJ IDEA が提供するプラグインでデコンパイルできます。

これで上記の説明が裏付けられます。明らかに String each=(String) var3.next(); の部分で型変換例外が発生しています。

3. ソリューション

3.1 推測の検証

何らかの方法で fastjson にジェネリクスをパラメータとして渡し、fastjson の戻り値にジェネリクス情報を持たせることで、この警告を解決できるのではないかと推測します。

当然、ソースコードを確認する必要があります。JSONObject クラスに次のメソッドが見つかりました。

第 2 部の分析を踏まえれば、容易に想像がつくでしょう。

JSONObject.parseObject(jsonString, List.class) で構築される List には JSONObject の要素が格納されています。foreach ループの内部では、イテレータを使用して各要素を走査し、User 型に強制的に変換しようとするため、型変換例外が発生します。

では、なぜ fastjson は List 型に自動変換できないのでしょうか。

「ジェネリクス消去によりジェネリクス情報が存在しないため、元の型を再構築できない」と言う人もいます。

しかし実際には、JSONObject.parseObject(jsonString, List.class) を見てください。第 1 パラメータは文字列で、第 2 パラメータは List.class です。fastjson にはジェネリクス情報が全く提供されていません。

ツール関数自体が、List にどのような型を格納すべきかを推測できるでしょうか。

したがって、何らかの方法でジェネリクス型の情報を伝えることができれば、fastjson は実際の型に正しく逆シリアル化できます。

`JSONObject.parseObject(jsonString, new TypeReference>(){

});` を使用すればよいのです。

つまり、TypeReference を使用することで、警告を排除するだけでなく、fastjson にジェネリクスの具体的な型を伝え、ジェネリクス型を正しく逆シリアル化できるのです。

では、その基盤となる原理は何でしょうか。見てみましょう。

コードとコメントから、次のことがわかります。

空の匿名サブクラスを作成します。型パラメータを匿名継承構造に埋め込むことで、ランタイムで型が消去されても再構築できます。

parseObject 関数に戻ると、内部でこの型情報が使用されていることが確認できます。

3.2 一つのことから他を推測する

他の多くのフレームワークでも、同様の方法でジェネリクス型を取得しています。

gson ライブラリを見てみましょう。

com.google.gson.reflect.TypeToken クラスを見てください。見覚えがありませんか。

さらに、JSON 逆シリアル化のシナリオに限らず、ジェネリクスパラメータを取得する同様の要件がある場合、同じ方法を使用できるでしょうか。

4. まとめ

IntelliJ IDEA の警告に注意を払うことが大切です。

問題に直面したときは、より合理的な視点から考え、問題の本質を理解しましょう。

一つの事例から他の事例を推測し、問題を学ぶ際に柔軟に応用しましょう。

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00

phone お問い合わせ
Hi, I'm Alibaba Cloud AI Assistant!
I can help with questions and solutions.