Java избыточное приведение требуется в универсальном методе - PullRequest
5 голосов
/ 24 сентября 2019

Я искал способ оптимизировать некоторые вещи в нашей базе кода с помощью универсальных функций.Существует функция с типом возвращаемого значения List<Object>, которая может иметь тип возвращаемого значения List<SpecifiedType>.

Ниже приведена минималистская версия этой функции, называемая function.Он принимает тип параметра, основываясь на том, что он вызывает соответствующую функцию (в данном случае ограниченную String) или универсальную функцию.

public static ArrayList<String> forString(){
    ArrayList<String> res = new ArrayList<>();
    // Fetching and processing data specific to String
    return res;
}

public static <T> ArrayList<T> forGeneric(Class<T> type){
    ArrayList<T> res = new ArrayList<>();
    // Fetching data
    return res;
}

public static <T> ArrayList<T> function(Class<T> type){
    if(type == String.class)
        return (ArrayList<T>) forString();
    return forGeneric(type);
}

Цель состоит в том, чтобы указанная выше функция вызывалась следующим образом: ArrayList<SomeType> someTypes = function(SomeType.class);

В коде выше я заметил две вещи:

  1. Требуется приведение к ArrayList<T>, хотя мы знаем, что если в качестве параметра передается тип Stringон вернет ArrayList<String> точно так же, как forString() метод

  2. Приведение к ArrayList<T> дает Unchecked cast предупреждение, даже если тип возврата будет ArrayList<String>

Мой вопрос: есть ли лучший способ сделать это (желательно без приведения), а если нет, то почему

Ответы [ 2 ]

4 голосов
/ 24 сентября 2019

Во-первых, это утверждение логически неверно

if(type.isInstance(String.class))

Если type равно Class<String>, то isInstance проверяет, является ли аргумент экземпляром строки.Передаваемый вами аргумент является экземпляром класса (в частности, Class<String>).

Если вы предпочитаете,

String.class.isInstance(String.class) == false

То, что вы имели в виду, было

if(type == String.class)

Однако даже после устранения этой логической ошибки в вашем коде по-прежнему будет отображаться предупреждение о неконтролируемом приведении.

Недостающая часть находится здесь

Требуется приведение к ArrayList<T>хотя мы знаем , что если тип String передан в качестве параметра, он вернет ArrayList<String>, как forString() method

Точно. Мы знаем это.Но то, что мы знаем и то, что знает компилятор, это две разные вещи.Компилятор не достаточно умен, чтобы проверить условия и понять, что с типом все в порядке.Возможно, может быть достаточно умным, но это не так.

Именно поэтому это проявляется как предупреждение, а не как ошибка.Это предупреждение, потому что то, что вы делаете, потенциально неправильно;это не определенно неправильно, иначе он не будет компилироваться вообще.В этом случае предупреждение должно послужить подсказкой для вас, чтобы еще раз проверить, что вы делаете правильно, и тогда вы можете счастливо подавить это.

@SuppressWarnings("unchecked")
public static <T> ArrayList<T> function(Class<T> type){
    if(type == String.class)
        return (ArrayList<T>) forString();
    return forGeneric(type);
}

Наконец - и это может быть артефактом вашего надуманного примера - но все эти методы бесполезны.Кажется, нет никакого преимущества перед прямым набором new ArrayList<>().Во время выполнения фактические экземпляры идентичны независимо от того, из какого из 3 методов они поступили.

0 голосов
/ 24 сентября 2019

Код времени выполнения не может возвращать различные универсальные типы на основе информации времени выполнения, потому что шаблоны являются механизмом времени компиляции.

Чтобы быть по-настоящему свободным от предупреждений компилятора, я бы возвратил новый список:

public static <T> ArrayList<T> function(Class<T> type){
    ArrayList<?> result;
    if (type.equals(String.class)) {
        result = forString();
    } else {
        result = forGeneric(type);
    }

    ArrayList<T> typedResult = new ArrayList<>(result.size());
    for (Object item : result) {
        typedResult.add(type.cast(item));
    }

    return typedResult;
}

Это может показаться расточительным, но это не так.Новый список - это не список новых объектов, а просто новые ссылки.Ссылка довольно мала, вероятно, не более 256 байт, поэтому список из 4000 объектов может занимать не более мегабайта оперативной памяти.

...