为什么杰克逊的TypeReference是抽象的?它通常会使用法更丑陋,因为您需要包含大括号来创建内联类(并且大多数 linter 会强制您在同一行上不使用大括号(。
https://github.com/FasterXML/jackson-core/blob/master/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java
new TypeReference<Map<String, String>>() {
};
这是由于一些晦涩的Java语言限制吗?
JB Nizet已经回答了原因。我只是想展示行动的原则。
您可以在其他课程中自己尝试此操作:
List<String> a = new ArrayList<String>();
List<String> b = new ArrayList<String>() {};
System.out.println(a.getClass().getGenericSuperclass());
System.out.println(b.getClass().getGenericSuperclass());
Ideone demo
输出:
java.util.AbstractList<E>
java.util.ArrayList<java.lang.String>
如您所见,创建匿名子类会保留有关列表的具体泛型类型在运行时的类型信息。
TypeReference
的作用大致相同:
new TypeReference<Map<String, String>>() { }
有一个泛型超类:
TypeReference<java.util.Map<java.lang.String, java.lang.String>>
由此,您可以获得类型Map<String, String>
.如果为另一种类型创建了类型引用:
new TypeReference<Map<Integer, Integer>>() { };
你可以得到类型Map<Integer, Integer>>
,它与Map<String, String>
是分开的。
如果TypeReference
是非抽象的,你可以这样写:
TypeReference<Map<String, String>> p = new TypeReference<>();
TypeReference<Map<Integer, Integer>> q = new TypeReference<>();
但是p
和q
的类型是无法区分的。
正是为了强制你创建一个子类,因为这是捕获泛型类型的唯一方法。
它写在你链接到的javadoc的第一句话中:
此泛型抽象类用于通过子类化获取完整的泛型类型信息。
(强调我的(。
该文档还包含指向解释该原则的博客文章的链接。
这个问题本身已经得到了回答,但我想为由于不得不使用大括号并占用额外一行而导致代码丑陋的问题提供解决方案。
通常它是这样用的:
Map<String, String> map = objectMapper.readValue(file, new TypeReference<>() {
});
这还不错,但对某些人来说可能会很烦人。因此,有一种方法可以通过使用JavaType
.您可以创建实用程序方法:
private <K, V> JavaType mapType(Class<K> keyClass, Class<V> valueClass) {
return objectMapper.getTypeFactory().constructParametricType(Map.class, keyClass, valueClass);
}
并像这样使用它:
Map<String, String> map = objectMapper.readValue(file, mapType(String.class, String.class));
对于列表和其他集合,也可以执行相同的操作。