【问题标题】:ObjectMapper can generate a list of invalid types (java8)ObjectMapper 可以生成无效类型列表(java8)
【发布时间】:2019-05-30 23:09:04
【问题描述】:

这与其说是代码出了什么问题,不如说是java为什么允许这段代码工作?

使用 com.fasterxml.jackson.databind.ObjectMapper 可以创建一个列表,其中填充的数据类型对于声明的列表应该是无效的。

我告诉它将List<Potato> 转换为List<Potato> 并将结果存储在List<Tractor>

public class ObjectMapperExample {
    public static void main(String[] args){
        List<Potato> potatoList = new ArrayList<>();
        //add a potato to the potatoList

        List<Tractor> tractorList = new ObjectMapper()
                .convertValue(potatoList, new TypeReference<List<Potato>>() {});
        //add another tractor to the tractorList

        System.out.println(tractorList.getClass());
        System.out.println(tractorList.toString());
        System.out.println(tractorList.get(1).getClass());
        System.out.println(tractorList.get(0).getClass());
    }
}

土豆类

public class Potato {
   // properties and getters/setters
}

拖拉机.class

public class Tractor {
   // properties and getters/setters
}

此代码实际上会编译并运行,直到您尝试直接与tractorList 中的Potato 对象交互。结果输出...

类 java.util.ArrayList

[土豆@20e2cbe0,拖拉机@68be2bc2]

类拖拉机

线程“main”java.lang.ClassCastException 中的异常:无法在 ObjectMapperExample.main(ObjectMapperExample.java:27) 处将土豆转换为 Tractor

所以问题是,为什么 java 仍然运行它?具有Potato 对象的List&lt;Tractor&gt; 不应该作为第一个元素在某处引发异常吗?直接访问元素时会抛出异常,这是有道理的,但是它是如何设法创建一个具有不兼容对象类型的 List 开始的呢?

这是我实际运行的屏幕截图...

【问题讨论】:

    标签: java objectmapper


    【解决方案1】:

    因为ObjectMapper::convertValue 在泛型声明中是虚假的:

    public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef) 
      throws IllegalArgumentException
    public <T> T convertValue(Object fromValue, JavaType toValueType)
      throws IllegalArgumentException
    

    TTypeReference&lt;?&gt;之间没有关系:编译器通过上下文推断T的真实类型,因为上下文是:

    List<Tractor> tractorList = new ObjectMapper().convertValue(potatoList, new TypeReference<List<Potato>>() {});
    

    它试图推断T的类型:List&lt;Tractor&gt;匹配和TypeReference&lt;List&lt;Potato&gt;&gt;匹配TypeReference&lt;?&gt;TypeReference可能与输出类型完全无关,这就是它能够创建@列表的原因987654333@。并且由于类型擦除启动,这在虚拟机中是可能的。

    似乎同样适用于使用JavaType的方法。

    另一个使用Class&lt;T&gt; 的方法不适用于您的情况,因为您不能使用List&lt;Tractor&gt;.class

    [edit] 如果您希望重现相同类型的问题,那么您可以这样做:

    List<Potato> potatoes = asList(new Potato());
    List<Tractor> tractors = badStuff(potatoes);
    
    private static <T> T badStuff(Object value) {
      return (T) value; // should raise a warning due to the cast
    }
    

    T 是从上下文中推断出来的,在该上下文中,这将是 List&lt;Tractor&gt;。一些编译器可能会对此感到窒息(Javac 可能会接受这一点,而 Eclipse 编译器不会接受,反之亦然)并需要精确类型:

    List<Tractor> tractors = MyType.<List<Tractor>>badStuff(potatoes);
    

    如果您想更深入地了解,这可能会有所帮助:type inference

    【讨论】:

    • 我不确定这是否能回答问题。问题不是“我如何使用 ObjectMapper”,而是“为什么可以构建类型不正确的对象的 List”。
    • 我的答案分为两部分:第一部分是关于类型擦除,以及为什么在这种情况下它会失败,第二部分是关于使用 Jackson API:它没有这样做是因为TypeReference don'没有关于列表中对象类型的足够信息。
    • 这并不能解释为什么可以在 java 中创建具有不兼容类型的 List。
    • 我更喜欢不涉及 ObjectMapper 的解释。我在我的例子中使用它只是因为我不知道如何复制这个问题(因此我在这里问它为什么会发生)。最终结果是 List 中的元素不符合其声明的类型。如果可以在没有 ObjectMapper 的情况下重新创建它(这应该是可能的,除非我弄错了)并解释了为什么它是可能的,那将满足我的问题。如果没有 ObjectMapper 就无法重新创建此问题(我对此表示怀疑),请解释原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2016-03-21
    • 2014-12-04
    • 1970-01-01
    相关资源
    最近更新 更多