これは、なにをしたくて書いたもの?
Jacksonを使ってJSONをデシリアライズする時に、ObjectMapper#readValue
をよく使うわけですが。そういえば、自分で
書いている時にList
やMap
といったジェネリックな型にデシリアライズしたことがないな、と思い。
工夫が要りそうだなと思い、ちょっと調べてみることに。
デシリアライズ時に型情報を与える
まずは、ObjectMapper#readValue
(第2引数がClass
クラスの方)のJavadocを見てみます。
readValue(JsonParser p, Class
よくよく見ると、こんなことが書いてあります。
Note: this method should NOT be used if the result type is a container (Collection or Map. The reason is that due to type erasure, key and value types cannot be introspected when using this method.
結果型をCollection
やMap
といったコンテナ型にする場合、このメソッドは使ってはいけません、と。
型情報がなくなるので、キーや値の型がわからなくなるからですね。
このような時は、以下のメソッド(第1引数がString
などの他のバリエーションのものも含めて)を使うのが良さそうです。
readValue(JsonParser p, TypeReference
readValue(JsonParser p, JavaType valueType)
TypeReference
メソッドの説明を見ると、今回の用途にはこちらを使うのがまずは良いのでしょうか?
Method to deserialize JSON content into a Java type, reference to which is passed as argument. Type is passed using so-called "super type token" (see ) and specifically needs to be used if the root type is a parameterized (generic) container type.
readValue(JsonParser p, TypeReference
パラメーター化されたコンテナ型をルート型に要求される場合、こちらを使うように、だそうです。
ここで使うものがTypeReference
クラスで、サブクラスを作成する時に型情報を埋め込みます。
Javadocの例からですが、こんな感じに使います。
TypeReference ref = new TypeReference<List<Integer>>() { };
こちらを、ObjectMapper#readValue
の第2引数に渡せばOKです。
もしくは、TypeFactory
を使ってJavaType
に変換して使います。
which can be passed to methods that accept TypeReference, or resolved using TypeFactory to obtain ResolvedType.
JavaType
もうひとつが、JavaType
を使う方法ですね。
readValue(JsonParser p, JavaType valueType)
TypeFactory
を使って直接JavaType
を組み立ててもよいですし、TypeReference
から変換する方法もあるようです。
※TypeReference
から変換する場合も、TypeFactory
を使用します。
こんな感じに使うようです。
ObjectMapper mapper = new ObjectMaper(); JavaType stringCollection = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
では、それぞれ使っていってみましょう。
環境
今回の環境は、こちら。
$ java --version openjdk 11.0.11 2021-04-20 OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04) OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.11, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-74-generic", arch: "amd64", family: "unix"
Mavenでの依存関係などは、このように定義。
<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.3</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.7.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.19.0</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> </plugin> </plugins> </build>
動作確認は、テストコードで行います。
テストコードの雛形とお題
テストコードの雛形は、こちら。
src/test/java/org/littlewings/jackson/DeserializeJsonWithTypeTest.java
package org.littlewings.jackson; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.MapType; import com.fasterxml.jackson.databind.type.TypeFactory; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class DeserializeJsonWithTypeTest { // ここに、テストを書く! }
お題としては、以下のクラスを題材に、List
やMap
に格納したインスタンスをJSONにシリアライズ、デシリアライズする
パターンをいくつか試してみようと思います。
src/test/java/org/littlewings/jackson/Person.java
package org.littlewings.jackson; public class Person { String lastName; String firstName; int age; public static Person create(String lastName, String firstName, int age) { Person person = new Person(); person.setLastName(lastName); person.setFirstName(firstName); person.setAge(age); return person; } // getter/setterは省略 }
List
まずはList
で試してみましょう。
Classを指定する
最初は、ObjectMapper#readValue
にClass
を指定してみます。
@Test public void nonProvideTypeAsList() throws JsonProcessingException { List<Person> persons = List.of( Person.create("磯野", "カツオ", 11), Person.create("フグ田", "タラオ", 3) ); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("[{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}]"); List<Object> deserializedPersons = mapper.readValue(json, List.class); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(0)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(0)).extracting("lastName").isEqualTo("磯野"); assertThat(deserializedPersons.get(0)).extracting("firstName").isEqualTo("カツオ"); assertThat(deserializedPersons.get(0)).extracting("age").isEqualTo(11); assertThat(deserializedPersons.get(1)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(1)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(1)).extracting("lastName").isEqualTo("フグ田"); assertThat(deserializedPersons.get(1)).extracting("firstName").isEqualTo("タラオ"); assertThat(deserializedPersons.get(1)).extracting("age").isEqualTo(3); }
このようにObjectMapper#readValue
の第2引数にList.class
とか渡してしまうと、その中に入るのはこのケースだと
LinkedHashMap
のインスタンスになります。
List<Object> deserializedPersons = mapper.readValue(json, List.class); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(0)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(0)).extracting("lastName").isEqualTo("磯野"); assertThat(deserializedPersons.get(0)).extracting("firstName").isEqualTo("カツオ"); assertThat(deserializedPersons.get(0)).extracting("age").isEqualTo(11); assertThat(deserializedPersons.get(1)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(1)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(1)).extracting("lastName").isEqualTo("フグ田"); assertThat(deserializedPersons.get(1)).extracting("firstName").isEqualTo("タラオ"); assertThat(deserializedPersons.get(1)).extracting("age").isEqualTo(3);
こんな感じに書いてもコンパイル自体は通りますが、List
に格納されたデータを扱う時にキャストに失敗します。
List<Person> deserializedPersons = mapper.readValue(json, List.class);
こちらが、その時の例外メッセージ。
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.littlewings.jackson.Person (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; org.littlewings.jackson.Person is in unnamed module of loader 'app')
もうちょっと具体的に型を指定したとしても、これくらいでしょうか。
List<LinkedHashMap<String, Object>> deserializedPersons = mapper.readValue(json, List.class);
TypeReferenceを使う
次は、TypeReference
を使ってみましょう。
@Test public void provideTypeReferenceAsList() throws JsonProcessingException { List<Person> persons = List.of( Person.create("磯野", "カツオ", 11), Person.create("フグ田", "タラオ", 3) ); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("[{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}]"); List<Person> deserializedPersons = mapper.readValue(json, new TypeReference<>() { }); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(0).getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get(0).getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get(0).getAge()).isEqualTo(11); assertThat(deserializedPersons.get(1)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(1).getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get(1).getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get(1).getAge()).isEqualTo(3); }
今回は、ObjectMapper#readValue
の第2引数にいきなりTypeReference
のサブクラスを作成して渡しています。
List<Person> deserializedPersons = mapper.readValue(json, new TypeReference<>() {
});
すると、List
の中に格納されるのがPerson
のインスタンスになります(LinkedHashMap
ではなく)。
assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(0).getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get(0).getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get(0).getAge()).isEqualTo(11); assertThat(deserializedPersons.get(1)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(1).getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get(1).getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get(1).getAge()).isEqualTo(3);
JavaTypeを使う
続いて、JavaType
。
@Test public void provideJavaTypeAsList() throws JsonProcessingException { List<Person> persons = List.of( Person.create("磯野", "カツオ", 11), Person.create("フグ田", "タラオ", 3) ); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("[{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}]"); TypeFactory typeFactory = mapper.getTypeFactory(); CollectionType collectionType = typeFactory.constructCollectionType(List.class, Person.class); List<Person> deserializedPersons = mapper.readValue(json, collectionType); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(0).getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get(0).getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get(0).getAge()).isEqualTo(11); assertThat(deserializedPersons.get(1)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(1).getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get(1).getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get(1).getAge()).isEqualTo(3); }
こんな感じで、ObjectMapper
からTypeFactory
を取得して、TypeFactory#construct〜Type
を使ってJavaType
を
構築します。
TypeFactory typeFactory = mapper.getTypeFactory(); CollectionType collectionType = typeFactory.constructCollectionType(List.class, Person.class); List<Person> deserializedPersons = mapper.readValue(json, collectionType);
construct〜Type
なメソッドは、配列、コレクション、Map
、ParameticType
などいろいろあります。
TypeReferenceをJavaTypeに変換して使う
最後に、TypeReference
をJavaType
に変換してみましょう。
@Test public void provideTypeReferenceToJavaTypeAsList() throws JsonProcessingException { List<Person> persons = List.of( Person.create("磯野", "カツオ", 11), Person.create("フグ田", "タラオ", 3) ); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("[{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}]"); TypeReference<List<Person>> typeReference = new TypeReference<>() { }; JavaType javaType = mapper.getTypeFactory().constructType(typeReference); List<Person> deserializedPersons = mapper.readValue(json, javaType); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(0).getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get(0).getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get(0).getAge()).isEqualTo(11); assertThat(deserializedPersons.get(1)).isInstanceOf(Person.class); assertThat(deserializedPersons.get(1).getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get(1).getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get(1).getAge()).isEqualTo(3); }
こんな感じで、TypeReference
のサブクラスのインスタンスを作成した後に、TypeFactory#constructType
を使って
JavaType
を構築することができます。
TypeReference<List<Person>> typeReference = new TypeReference<>() {
};
JavaType javaType = mapper.getTypeFactory().constructType(typeReference);
List<Person> deserializedPersons = mapper.readValue(json, javaType);
だいたい、使い方はわかった気がしますね。
Mapで使う
もうひとつ、Map
でバリエーションを試してみましょう。
Classを指定する
まずは、Class
をObjectMapper#readValue
に指定するパターン。
@Test public void nonProvideTypeAsMap() throws JsonProcessingException { Map<String, Person> persons = new LinkedHashMap<>(); persons.put("katsuo", Person.create("磯野", "カツオ", 11)); persons.put("tarao", Person.create("フグ田", "タラオ", 3)); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("{\"katsuo\":{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},\"tarao\":{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}}"); Map<String, Object> deserializedPersons = mapper.readValue(json, Map.class); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get("katsuo")).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get("katsuo")).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get("katsuo")).extracting("lastName").isEqualTo("磯野"); assertThat(deserializedPersons.get("katsuo")).extracting("firstName").isEqualTo("カツオ"); assertThat(deserializedPersons.get("katsuo")).extracting("age").isEqualTo(11); assertThat(deserializedPersons.get("tarao")).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get("tarao")).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get("tarao")).extracting("lastName").isEqualTo("フグ田"); assertThat(deserializedPersons.get("tarao")).extracting("firstName").isEqualTo("タラオ"); assertThat(deserializedPersons.get("tarao")).extracting("age").isEqualTo(3); }
こちらは、値がLinkedHashMap
なMap
となります。
List<Object> deserializedPersons = mapper.readValue(json, List.class); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get(0)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(0)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(0)).extracting("lastName").isEqualTo("磯野"); assertThat(deserializedPersons.get(0)).extracting("firstName").isEqualTo("カツオ"); assertThat(deserializedPersons.get(0)).extracting("age").isEqualTo(11); assertThat(deserializedPersons.get(1)).isNotInstanceOf(Person.class); assertThat(deserializedPersons.get(1)).isInstanceOf(LinkedHashMap.class); assertThat(deserializedPersons.get(1)).extracting("lastName").isEqualTo("フグ田"); assertThat(deserializedPersons.get(1)).extracting("firstName").isEqualTo("タラオ"); assertThat(deserializedPersons.get(1)).extracting("age").isEqualTo(3);
TypeReferenceを使う
TypeReference
を使った場合は、こんな感じに。
@Test public void provideTypeReferenceAsMap() throws JsonProcessingException { Map<String, Person> persons = new LinkedHashMap<>(); persons.put("katsuo", Person.create("磯野", "カツオ", 11)); persons.put("tarao", Person.create("フグ田", "タラオ", 3)); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("{\"katsuo\":{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},\"tarao\":{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}}"); Map<String, Person> deserializedPersons = mapper.readValue(json, new TypeReference<>() { }); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get("katsuo")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("katsuo").getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get("katsuo").getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get("katsuo").getAge()).isEqualTo(11); assertThat(deserializedPersons.get("tarao")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("tarao").getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get("tarao").getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get("tarao").getAge()).isEqualTo(3); }
List
の時と同じように、TypeReference
でMap
に関する型情報を指定してObjectMapper#readValue
に与えることで、
Map<String, Person>
としてデシリアライズできます。
Map<String, Person> deserializedPersons = mapper.readValue(json, new TypeReference<>() { }); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get("katsuo")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("katsuo").getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get("katsuo").getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get("katsuo").getAge()).isEqualTo(11); assertThat(deserializedPersons.get("tarao")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("tarao").getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get("tarao").getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get("tarao").getAge()).isEqualTo(3);
JavaTypeを使う
JavaType
を使った場合。
@Test public void provideJavaTypeAsMap() throws JsonProcessingException { Map<String, Person> persons = new LinkedHashMap<>(); persons.put("katsuo", Person.create("磯野", "カツオ", 11)); persons.put("tarao", Person.create("フグ田", "タラオ", 3)); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("{\"katsuo\":{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},\"tarao\":{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}}"); TypeFactory typeFactory = mapper.getTypeFactory(); MapType mapType = typeFactory.constructMapType(Map.class, String.class, Person.class); Map<String, Person> deserializedPersons = mapper.readValue(json, mapType); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get("katsuo")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("katsuo").getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get("katsuo").getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get("katsuo").getAge()).isEqualTo(11); assertThat(deserializedPersons.get("tarao")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("tarao").getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get("tarao").getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get("tarao").getAge()).isEqualTo(3); }
Map
を対象とする場合は、TypeFactory#constructMapType
を使います。
TypeFactory typeFactory = mapper.getTypeFactory(); MapType mapType = typeFactory.constructMapType(Map.class, String.class, Person.class); Map<String, Person> deserializedPersons = mapper.readValue(json, mapType);
TypeReferenceからJavaTypeに変換して使う
最後は、TypeReference
からJavaType
に変換してObjectMapper#readValue
に適用します。
@Test public void provideTypeReferenceToJavaTypeAsMap() throws JsonProcessingException { Map<String, Person> persons = new LinkedHashMap<>(); persons.put("katsuo", Person.create("磯野", "カツオ", 11)); persons.put("tarao", Person.create("フグ田", "タラオ", 3)); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(persons); assertThat(json).isEqualTo("{\"katsuo\":{\"lastName\":\"磯野\",\"firstName\":\"カツオ\",\"age\":11},\"tarao\":{\"lastName\":\"フグ田\",\"firstName\":\"タラオ\",\"age\":3}}"); TypeReference<Map<String, Person>> typeReference = new TypeReference<>() { }; JavaType javaType = mapper.getTypeFactory().constructType(typeReference); Map<String, Person> deserializedPersons = mapper.readValue(json, javaType); assertThat(deserializedPersons).hasSize(2); assertThat(deserializedPersons.get("katsuo")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("katsuo").getLastName()).isEqualTo("磯野"); assertThat(deserializedPersons.get("katsuo").getFirstName()).isEqualTo("カツオ"); assertThat(deserializedPersons.get("katsuo").getAge()).isEqualTo(11); assertThat(deserializedPersons.get("tarao")).isInstanceOf(Person.class); assertThat(deserializedPersons.get("tarao").getLastName()).isEqualTo("フグ田"); assertThat(deserializedPersons.get("tarao").getFirstName()).isEqualTo("タラオ"); assertThat(deserializedPersons.get("tarao").getAge()).isEqualTo(3); }
こちらはList
の時と同様に、TypeFactory#constructType
を使ってTypeReference
を元にJavaType
を構築すればOKです。
TypeReference<Map<String, Person>> typeReference = new TypeReference<>() {
};
JavaType javaType = mapper.getTypeFactory().constructType(typeReference);
Map<String, Person> deserializedPersons = mapper.readValue(json, javaType);
まとめ
Jacksonを使って、型引数を持ったクラスにデシリアライズする方法を見てみました。
あまり考えたことがなかったのと、調べようとしてもちょっと見つけにくかった感じがしたので、自分でもまとめつつ
Javadocも眺めてみました。
調べるとTypeReference
の方が最初に見つかるのですが、Javadocを見ているとJavaType
のことに気づいたりするので、
見返してみると発見がありますね、と…。