これは、なにをしたくて書いたもの?
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のことに気づいたりするので、
見返してみると発見がありますね、と…。