使用泛型返回带有类型参数的对象并避免使用类型安全警告

dag*_*erx 8 java generics

我正在编写一个为我的环境提供一些常用工具的类.

在这个类中,我有以下接收JSON字符串的方法,并且应该将JSON映射到适当的对象:

public static <T> T mapJsonToObject(String json, Class<T> clazz) {
    ObjectMapper mapper = new ObjectMapper();
    T parsedObject = null;
    try {
        parsedObject = mapper.readValue(json, clazz);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return parsedObject;
}
Run Code Online (Sandbox Code Playgroud)

从String到适当的类的实际转换是使用Jackson库完成的,并且正常工作,以及整个方法.

调用此方法,例如:

Object o = MyUtilities.mapJsonToObject(jsonString, Object.class);
Point p = MyUtilities.mapJsonToObject(jsonString, Point.class);
PhoneBook pb = MyUtilities.mapJsonToObject(jsonString, PhoneBook.class);
Run Code Online (Sandbox Code Playgroud)

一切正常.

但是,如果我不想将JSON映射到特定的对象,我只想使用键/值对将其转换为映射,如下所示:

HashMap<String, Object> myMap = MyUtilities.mapJsonToObject(result, HashMap.class);
Run Code Online (Sandbox Code Playgroud)

"Type safety: The expression of type HashMap needs unchecked conversion to conform to HashMap<String,Object>"从编译器收到警告.这是因为我在使用HashMap.class时丢失了泛型参数

在写这个问题时,我实际上偶然发现了这个教程,并想到了以下面的方式重载映射方法:

public static <T> T mapJsonToObject(String json, T obj) {
    ObjectMapper mapper = new ObjectMapper();
    T parsedObject = null;
    try {
        parsedObject = mapper.readValue(json, new TypeReference<T>() {});
    } catch (Exception e) {
            e.printStackTrace();            
    }
    return parsedObject;
}
Run Code Online (Sandbox Code Playgroud)

这需要传递一个我更愿意避免的实际对象:

HashMap<String, Object> myMap = null;
myMap = MyUtilities.mapJsonToObject(result, myMap);
Run Code Online (Sandbox Code Playgroud)

知道如何改进这两种方法以避免传递Object和类型安全警告吗?请注意,我不是在寻找特定于HashMap的解决方案

Pav*_*ral 7

对于泛型类型,您将始终面临类型擦除的问题.作为一种解决方法,您可以执行未经检查的类型转换,如下所示:

@SuppressWarnings("unchecked")
public static <C, T extends C> C mapJsonToObject(String jsonString, Class<T> result) {
    // ... stuff ...
    return (C) parsedObject;
}
Run Code Online (Sandbox Code Playgroud)

然而,考虑到你不控制Jackson实际用于集合条目的类,这是非常丑陋的.

为了获得更好的结果,您可以使用Jackson的特殊类型系统:

@SuppressWarnings("unchecked")
public static <C> C mapJsonToObject(String jsonString, JavaType type) {
    // ... stuff ...
    return (C) parsedObject;
}

// Check answer comments on TypeFactory#collectionType deprecation
List<MyBean> result = mapJsonToObject("[]", TypeFactory.collectionType(ArrayList.class, MyBean.class));
Run Code Online (Sandbox Code Playgroud)

或者您可以使用杰克逊的类型参考:

public static <T> T mapJsonToObject(String jsonString, TypeReference<T> type) {
    // ... stuff ...
}

List<MyBean> result = mapJsonToObject("[]", new TypeReference<List<Object>>() {});
Run Code Online (Sandbox Code Playgroud)

我有点像第一种方法(通过JavaType),因为它不会为你编写的每个方法调用创建一个匿名类.


更新为什么第一个选项很难看:

第一个选项有效,因为泛型参数TC.之间的关系接近于零.虽然T将根据传递的参数C推断,但将根据结果赋值推断.它们之间唯一的限制是T必须扩展C.这只是一种形式,以防止完全不正确的电话,如String s = mapJsonToObject("", List.class).

选项很难看,因为你没有说任何关于地图内容的内容.即杰克逊要决定它想要使用哪种类.

例如,{ "foo": "xxx", "bar": 1 }可能会使用以下条目映射到Map:

  • 字符串"foo"=>字符串"xxx"
  • 字符串"bar"=>整数1

但是因为{ "foo": { "bar": 1 } }它可能是这样的:

  • 字符串"foo"=> ObjectNode { "bar": 1 }

您可以使用第一个选项.但除了Map<String, Object>没有嵌套对象或数组的简单JSON字符串之外,它永远不会有好处.