Когда допустимо передать аргумент 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?