【问题标题】:Deserialise using java generics使用java泛型反序列化
【发布时间】:2018-04-20 10:04:37
【问题描述】:

考虑这个外部执行类

public class ExternalCommands {

    private Logger log = LoggerFactory.getLogger(ExternalCommands.class);

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public <T> CustomResponse<T> executeQuery(Clients client, Query query, Class<T> classType) throws Exception {
        if (Objects.isNull(clients))
            throw new Exception("external client is null in external commands");

        log.debug("Query : {}", query);
        Response queryResponse = clients.getElasticClient().executeQuery(query);
        log.debug("query response : {}", queryResponse);
        if (queryResponse.status() == 200) {
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(CustomResponse.class, classType);  // This is the coding sin m talking about
            return objectMapper.readValue(queryResponse.body().asInputStream(), javaType);
        }
    }
}

所以基本上这个executeQuery 函数会根据query 从弹性客户端获取所有数据,并根据函数中发送的通用classType 对其进行反序列化
但是
这更像是明确地 使用deserialisation 而不是使用Generics


请参阅下面的代码,了解此执行如何与内联 cmets 一起工作:

public ArrayList<EmpData> getEmpData() throws Exception {
    ArrayList<EmpData> merchantUrnMap = new ArrayList<>();

    List<Filter> filters = new ArrayList<>();
    filters.add("Added filter 1 here");
    filters.add("Added filter 2 here");
    filters.add("Added filter 3 here");

    ExternalCommands commands = new ExternalCommands();
    Query query = commands.getQuery(filters);
    // get "clients"

    // this is how it works now
    CustomResponse<EmpData> response = commands.executeQuery(clients, query, EmpData.class);

    // this is how i WANT IT TO WORK - without passing "EmpData.class"
    // but in this case <T> in "CustomResponse<T>" would not deserialise to "EmpData"
    // resulting in LinkedHashMap in deseralised object instead of class object
    // CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query); 


    // some operations
    return response

}

关于如何实现这一点的任何建议?

【问题讨论】:

    标签: java class generics java-8


    【解决方案1】:

    案例 1:假设 constructParametricType 需要 classType 参数才能正常运行,并且您无法更改已发布的代码中隐含但未提供的方法/类的实现。

    由于 Java 中的 type erasure,您提议的方法签名/调用不可能。

    您在constructParametricType(CustomResponse.class, classType) 调用中使用了classType,并且试图以某种方式将classType 替换为T。这是不可能的,因为在编译代码时,T 会被完全擦除。没有办法做类似constructParametricType(CustomResponse.class, T.class) 的事情,因为T 在运行时不存在。

    正确的解决方案是将类作为方法参数传入,这正是您现有方法的作用。


    案例 2:您真的想要拨打电话 commands.&lt;EmpData&gt;executeQuery(clients, query);,并且您愿意进行任何更改以实现该目标。

    由于我们不能将T 作为参数传递给constructParametricType,所以它必须被称为constructParametricType(CustomResponse.class),但它需要返回一个代表CustomResponse&lt;T&gt;JavaType。唯一的办法就是声明

    <T> JavaType<T> constructParametricType(Class<?> cls)
    

    请注意,JavaType 现在也必须出于同样的原因进行参数化(我们无法在运行时获得T)。最后,我们要声明

    CustomResponse<T> readValue(InputStream stream, JavaType<T> javaType)
    

    匹配声明的返回类型executeQuery

    在所有这些更改之后,行

    CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query);
    

    应该编译。这是一个最小的例子:

    class CustomResponse<T> {}
    class Clients{}
    class Query{}
    class EmpData{}
    
    class ObjectMapper {
        JavaTypeFactory getTypeFactory() {
            return new JavaTypeFactory();
        }
        <T> CustomResponse<T> readValue(InputStream s, JavaType<T> j) {
            return new CustomResponse<>();
        }
    }
    
    class JavaTypeFactory {
        <T> JavaType<T> constructParametricType(Class<?> cls) {
            return new JavaType<>(cls);
        }
    }
    
    class JavaType<T> {
        JavaType(Class<?> cls) {}
    }
    
    class ExternalCommands {
        private static final ObjectMapper objectMapper = new ObjectMapper();
    
        public <T> CustomResponse<T> executeQuery(Clients clients, Query query) throws Exception {
            InputStream queryResponseStream = null;
            JavaType<T> javaType = objectMapper.getTypeFactory().<T>constructParametricType(CustomResponse.class);
            return objectMapper.readValue(queryResponseStream, javaType);
        }
    }
    
    class SomeClass {
        public void getEmpData() throws Exception {
            ExternalCommands commands = new ExternalCommands();
            Query query = null;
            Clients clients = null;
            CustomResponse<EmpData> response = commands.<EmpData>executeQuery(clients, query);
        }
    }
    

    请注意,鉴于系统的其余部分(尤其是参数化JavaType),所描述的某些更改可能并不容易/不可能,我不推荐这种方法。我建议坚持你所拥有的;这是 IMO 最干净的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-18
      • 2013-08-26
      相关资源
      最近更新 更多