Когда допустимо передать аргумент Class <T> универсальному методу?

Методы, которые являются общими с помощью параметра T, наверняка могут быть полезны. Тем не менее, мне любопытно, как использовать общий метод, если вы передадите аргумент, например Class<T> clazz, методу. Я придумал случай, который, возможно, может быть возможным. Возможно, вы хотите только запустить часть метода, основанную на типе класса. Например:

/** load(File, Collection<T>, Class<T>)
* Creates an object T from an xml.  It also prints the contents of the collection if T is a House object.
* @return T
* Throws Exception
*/
private static <T> T void load(File xml, Collection<T> t, Class<T> clazz) throws Exception{
    T type = (T) Jaxb.unmarshalFile(xml.getAbsolutePath(), clazz);  // This method accepts a class argument.  Is there an alternative to passing the class here without "clazz"?  How can I put "T" in replace of "clazz" here?
    if (clazz == House.class) {
         System.out.println(t.toString());
    } else {
         t.clear();
    }
    return T;
}

Это общепринятая практика? Когда аргумент Class<T> clazz полезен для общих методов?

Ответ 1

Это общепринятая практика?

Ну, мне.. нет не совсем. Для меня это кажется несколько бессмысленным, когда вы можете просто определить некоторые границы для типа T. Например:

private static <T extends House> void load(Collection<T> t)

Это гарантирует, что либо объект имеет тип House, либо подкласс House, но опять же, если вам нужен только экземпляр типа House или его подклассы, это должно быть просто:

private static void load(Collection<House> houses)

Идея генериков заключается в том, чтобы сделать метод или класс более гибкими и расширяемыми, поэтому мне кажется противоречивым начать сравнивать типы классов в теле метода, когда само понятие дженериков абстрагируется от таких подробности.

Ответ 2

Я бы передал только объекты класса, если общий тип не мог быть получен иначе. В вашем случае компилятор должен иметь возможность вывести T из коллекции. Чтобы обрабатывать конкретные объекты по-разному, я бы использовал полиморфизм - например, House#something() и Other#something(), и просто вызовите anyObject.something().

Ответ 3

Я думаю, что это приемлемо , но, если его можно избежать, тогда вам нужно. Как правило, если у вас могут быть разные методы, которые принимают разные типы, то делайте это вместо одного метода, который использует предложения if для выполнения чего-то другого в зависимости от типа параметра. Вы также можете делегировать классу операцию, которую вы хотите настроить для определенного типа.

В вашем случае вы можете просто проверить тип каждого элемента коллекции с помощью instanceof, чтобы сделать то, что вам нужно для определенного типа. Но он не будет работать, если список пуст.

Типичное использование - если вам нужно получить тип для его создания, и вы можете найти его по-другому. Например, Spring использует его для загружает bean из своего имени:

<T> T getBean(Class<T> requiredType)

В этом случае его нельзя избежать (без необходимости отбрасывать).

Ответ 4

Если возвращаемое значение или другие типы параметров зависят или должны быть равны, дженерики будут добавлять проверки времени компиляции, так что нет необходимости передавать в T.

Примеры

<T> T createNewInstanceOfType(Class<T> type);


<T> void addValueToCollection(Collection<T> collection,T value);


<T> List<Class<? extends T>> findSubClassesInClasspath(Class<T> superType);

Необработанные типы

По-прежнему можно отложить ошибку кастинга до времени выполнения (ClassCastException) с некоторыми нажатиями, например. с неявными отбрасываниями из не общих (необработанных) типов в общие:

List nonGenericList = new ArrayList();
nonGenericList.add(new Integer(42));
List<String> wreckedList = nonGenericList;

Компилятор создаст кучу предупреждений, если вы не подавите их с аннотациями или настройками компилятора.

Настройки компилятора (Eclipse):

Например, использование типов raw генерирует предупреждение по умолчанию, можно обрабатывать предупреждения как ошибки и даже как фатальные ошибки:

enter image description here

Ответ 5

Вы передадите аргумент Class<T> в generics, если и только если вы передадите аргумент Class перед генериками. Другими словами, только если объект Class используется каким-то образом. Generics служит инструментом проверки типа компиляции. Однако, какие аргументы вы передаете, должно определяться логикой времени выполнения программы и не должно иметь никакого отношения к дженерикам.

Ответ 6

Я не видел передачи объекта Class, чтобы проверить тип среды выполнения как общий пример использования для генериков. Если вы это делаете, есть хороший шанс, что есть лучший способ настроить структуру вашего класса.

То, что я видел, - это если вам нужно создать новый экземпляр рассматриваемого класса или иначе использовать отражение. В этом случае вам нужно передать объект Class, потому что Java не может получить его во время выполнения благодаря стиранию типа.

Ответ 7

В вашем случае фактическое наличие параметра Generic строго не требуется. Поскольку вывод функции, которую вы описываете, не зависит от типа ввода, вы также можете использовать wild cards.

private static void stuff(Collection<?> t){
    Object next = t.iterator().next(); //this is ugly and inefficient though
    if(next instanceof House){  
        System.out.print(next.toString());
    }else{
        t.clear();
    }
}

Единственный раз, когда вы должны использовать общий параметр, - это когда тип результата функции будет зависеть от типа параметров.

Вам нужно будет передать класс, соответствующий типу, когда ваш код ему понадобится; большую часть времени это происходит, когда: - Вам нужно указать/проверить объекты проверки на T - Применяется сериализация/десериализация. - Вы не можете получить доступ к любому экземпляру T в своей функции, и вы не можете вызвать метод getClass(), когда вам это нужно.

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

Я ответил на аналогичную дискуссию в прошлом: Когда использовать общие методы и когда использовать wild-card?