ObjectMapper может генерировать список недопустимых типов (java8) - PullRequest
1 голос
/ 31 мая 2019

Вопрос не столько в том, что не так с кодом, сколько в том, почему 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());
    }
}

Potato.class

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

Tractor.class

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

Этот код фактически компилируется и выполняется до тех пор, пока вы не попытаетесь напрямую взаимодействовать с объектом Potato в tractorList. Результирующий вывод ...

класс java.util.ArrayList

[Картофель @ 20e2cbe0, Трактор @ 68be2bc2]

Трактор класса

Исключение в потоке "main" java.lang.ClassCastException: картофель не может быть брошен на трактор в ObjectMapperExample.main (ObjectMapperExample.java:27)

Итак, вопрос в том, почему Java все еще запускает это? Разве List<Tractor>, у которого есть Potato объект, как его первый элемент, не выдал где-то исключение? Вызывает исключение при непосредственном доступе к элементу, что имеет смысл, но как ему удалось создать Список с несовместимыми типами объектов для начала?

Вот скриншот того, что я сделал ... Test run

1 Ответ

0 голосов
/ 31 мая 2019

Поскольку ObjectMapper :: convertValue является поддельным в том, как были объявлены обобщения:

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

Нет никакого отношения между T и TypeReference<?>: компилятор выводит реальный тип T, используя контекст, и поскольку контекст:

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

Он пытается вывести тип T: List<Tractor> совпадения и TypeReference<List<Potato>> совпадения TypeReference<?>: TypeReference может быть совершенно не связан с типом вывода, поэтому он смог создать список Potato. И так как тип стирается, это возможно в виртуальной машине.

Кажется, то же самое применимо к методу, использующему JavaType.

Другое использование Class<T> не будет применяться в вашем случае в вашем случае, потому что вы не можете сделать List<Tractor>.class.

[править] Если вы хотите воспроизвести подобные проблемы, вы можете сделать это:

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<Tractor>. Некоторый компилятор может подавиться этим (Javac может принять это, а Eclipse Compiler - нет и наоборот) и потребовать уточнения типа:

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

Если вы хотите посмотреть более подробно, это, вероятно, поможет: вывод типа .

...