Класс литерал с дженериками в Java - PullRequest
0 голосов
/ 08 апреля 2019

Пройдя по следующим ссылкам, например link1 и link2 , я столкнулся с проблемой предоставления литерала класса в качестве параметра.Ниже приведен мой код, и я получаю сообщение об ошибке компиляции следующим образом:

class GenericService {
    public <T> List<T> serve(Class<T> clazz) {
        if(clazz == Set.class) {
            Set<String> inputs = new HashSet<>();
            return implement(inputs, clazz);
        } else if(clazz == Map.class) {
            Map<String, Object> inputs = new HashMap<>();
            return implement(inputs, clazz);
        }
    }

    public List<Set<String>> implement(Set<String> inputs, Type clazz) {
        //Based on clazz decide that the output will be of type 'List<Set<String>>'
        List<Set<String>> result = new ArrayList<>();
        //do something with inputs
        //populate result
        return result;
    }

    public List<Map<String, Object>> implement(Map<String, Object> inputs, Type clazz) {
        //Based on clazz decide that the output will be of type 'List<Map<String, Object>>' 
        List<Map<String, Object>> result = new ArrayList<>();
        //do something with inputs
        //populate result
        return result;
    }
}

Обратите внимание на пару вещей:

  1. Вместо этого я могу передать только List.class или Map.classтакже указание универсального типа.На основании этого определяется тип результата (подробности реализации не предоставлены).
  2. Поскольку тип результата получен с использованием class literal, я получаю следующие ошибки времени компиляции.

Type mismatch: cannot convert from List<Set<String>> to List<T>

Type mismatch: cannot convert from List<Map<String,Object>> to List<T>

Как я могу заставить это работать?

1 Ответ

1 голос
/ 08 апреля 2019

обо всем по порядку.

  1. Я могу передать только List.class или Map.class вместо того, чтобы указывать универсальный тип. На основании этого определяется тип результата (детали реализации не предоставлены).

Вы не можете указать универсальный тип в параметре класса, потому что (Прочтите это внимательно) Информация универсального типа не присутствует во время выполнения. Во время выполнения вы не получаете List<String> или List<Integer> и т. Д. Во время выполнения все просто List.


Краткая история о том, почему это так.

Обобщения не существовали до Java 5. Поэтому, когда дженерики были представлены как часть Java 5, последнее, что они хотели сделать, - это сказать всему миру, чтобы они изменились с List на List<String> или List<Whatever_Object_They_Want> ,

Например, , скажем, вы используете зависимость, которая была скомпилирована с использованием Java 4, и метод в этой зависимости, который возвращает просто старый List, вы все равно можете вызвать этот метод, используя свой код , который скомпилирован в Java 5. Единственное, что вы не можете привести его к какому-либо универсальному типу.

Представьте себе, если Java5 начал поддерживать только общие типы, такие как List <>, а не обычный старый List. Вы должны зависеть от всех поставщиков, чтобы обеспечить зависимость, которая скомпилирована в Java5. Это вызвало бы хаос. Таким образом, чтобы избежать этого и поддерживать обратную совместимость с теми, кто использовал предыдущие версии java, информация об общем типе удаляется, когда код компилируется в байтовый код. Это называется Type Erasure.

По этой причине нельзя указывать универсальный тип с литералами класса любого типа коллекции.


Подойдя ко второму пункту

  1. Поскольку тип результата получен с использованием литерала класса, я получаю следующие ошибки времени компиляции.

Несоответствие типов: невозможно преобразовать из List<Set<String>> в List<T>

Несоответствие типов: невозможно преобразовать из List<Map<String,Object>> в List<T>

При просмотре документации Generics универсальный тип List отличается от List< Map< String, Object >>>. (Вот почему у нас есть дженерики в Java. Например, List<String> отличается от List<Object>. Вы не можете привести List<String> к List<Object>)

Однако, так как вы хотите вернуть List<Map<>> в некоторых случаях и List<Set<>> в некоторых других случаях в зависимости от параметра, я предлагаю вам изменить тип возврата метода serve на List<? extends Collection>

Использование List<? extends Collection> в качестве аргумента возвращаемого типа / метода означает, что этот список может быть списком любого из подклассов Collection. Пожалуйста, взгляните на иерархию, чтобы получить больше информации.

Надеюсь, это помогло!

...