Как использовать класс <T> в Java?
Там хорошо обсуждается Generics и что они действительно делают за кулисами в этот вопрос, поэтому мы все знаем, что Vector<int[]>
- это вектор целочисленных массивов, а HashTable<String, Person>
- таблица ключей, которые являются строками и значениями Person
s.
Тем не менее, то, что претит меня, - это использование Class<>
.
Предполагается, что класс java Class
также принимает имя шаблона (или так мне говорит желтая подчеркивание в eclipse). Я не понимаю, что я должен там положить. Вся цель объекта Class
заключается в том, что вы не полностью имеете информацию об объекте, для отражения и т.д. Почему это заставляет меня указать, какой класс будет содержать объект Class
? Я явно не знаю, или я бы не использовал объект Class
, я бы использовал конкретный.
Ответы
Ответ 1
Использование расширенной версии класса Class позволяет вам, помимо прочего, писать такие вещи, как
Class<? extends Collection> someCollectionClass = someMethod();
а затем вы можете быть уверены, что объект Class, который вы получаете, расширяет Collection
, а экземпляр этого класса будет (по крайней мере) сборником.
Ответ 2
Все, что мы знаем: "Все экземпляры любого класса используют один и тот же объект java.lang.Class этого класса"
например,
Student a = new Student();
Student b = new Student();
Тогда a.getClass() == b.getClass()
верно.
Теперь предположим, что
Teacher t = new Teacher();
без дженериков возможно следующее.
Class studentClassRef = t.getClass();
Но сейчас это не так..?
например, public void printStudentClassInfo(Class studentClassRef) {}
можно вызвать с помощью Teacher.class
Этого можно избежать, используя дженерики.
Class<Student> studentClassRef = t.getClass(); //Compilation error.
Теперь, что такое T?? T - параметры типа (также называемые переменными типа); ограниченный угловыми скобками (& lt;>), следует за именем класса.
T - это просто символ, похожий на имя переменной (может быть любым именем), объявленной во время записи файла класса. Позже этот T будет заменен
действительным именем класса во время инициализации (HashMap<String> map = new HashMap<String>();
)
например) class name<T1, T2, ..., Tn>
Таким образом, Class<T>
представляет объект класса определенного типа класса 'T
'.
Предположим, что ваши методы класса должны работать с параметрами неизвестного типа, как показано ниже
/**
* Generic version of the Car class.
* @param <T> the type of the value
*/
public class Car<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Здесь T можно использовать как тип String
как CarName
.ИЛИ T можно использовать как тип Integer
как номер модели,
ИЛИ T можно использовать как тип Object
как допустимый экземпляр автомобиля.
Теперь выше приведен простой POJO, который можно использовать по-разному во время выполнения.
Коллекции, например, List, Set, Hashmap - лучшие примеры, которые будут работать с различными объектами согласно объявлению T, но как только мы объявили T как String
Например, HashMap<String> map = new HashMap<String>();
Тогда он будет принимать только объекты экземпляров String Class.
Общие методы
Универсальные методы - это методы, которые вводят свои собственные параметры типа. Это похоже на объявление универсального типа, но область действия параметра типа ограничена методом, в котором он объявлен. Разрешены статические и нестатические обобщенные методы, а также конструкторы обобщенных классов.
Синтаксис для универсального метода включает параметр типа, заключенный в angular скобки, и отображается перед типом, возвращаемым методом. Для универсальных методов раздел параметров типа должен отображаться перед возвращаемым типом метода.
class Util {
// Generic static method
public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
class Pair<K, V> {
private K key;
private V value;
}
Здесь <K, V, Z, Y>
- это объявление типов, используемых в аргументах метода, которые должны быть перед возвращаемым типом, который здесь boolean
.
Ниже; объявление типа <T>
не требуется на уровне метода, поскольку оно уже объявлено на уровне класса.
class MyClass<T> {
private T myMethod(T a){
return a;
}
}
Но ниже это неверно, так как параметры типа класса K, V, Z и Y нельзя использовать в статическом контексте (здесь статический метод).
class Util <K, V, Z, Y>{
// Generic static method
public static boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
ДРУГИЕ ДЕЙСТВУЮЩИЕ СЦЕНАРИИ
class MyClass<T> {
//Type declaration <T> already done at class level
private T myMethod(T a){
return a;
}
//<T> is overriding the T declared at Class level;
//So There is no ClassCastException though a is not the type of T declared at MyClass<T>.
private <T> T myMethod1(Object a){
return (T) a;
}
//Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).
private T myMethod1(Object a){
return (T) a;
}
// No ClassCastException
// MyClass<String> obj= new MyClass<String>();
// obj.myMethod2(Integer.valueOf("1"));
// Since type T is redefined at this method level.
private <T> T myMethod2(T a){
return a;
}
// No ClassCastException for the below
// MyClass<String> o= new MyClass<String>();
// o.myMethod3(Integer.valueOf("1").getClass())
// Since <T> is undefined within this method;
// And MyClass<T> don't have impact here
private <T> T myMethod3(Class a){
return (T) a;
}
// ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
// Should be o.myMethod3(String.valueOf("1").getClass())
private T myMethod3(Class a){
return (T) a;
}
// Class<T> a :: a is Class object of type T
//<T> is overriding of class level type declaration;
private <T> Class<T> myMethod4(Class<T> a){
return a;
}
}
И, наконец, статический метод всегда нуждается в явном объявлении <T>
; Это не будет производным от уровня класса Class<T>
. Это связано с тем, что уровень класса T связан с экземпляром.
Также ознакомьтесь с ограничениями на общие сведения
.
Подстановочные знаки и подтипы
введите аргумент для универсального метода
Ответ 3
Из документации Java:
[...]
Более удивительно, класс Class был расширен. Литералы классов теперь функционируют как токены типа, предоставляя информацию о времени выполнения и времени компиляции. Это позволяет использовать стиль статических фабрик, например, методом getAnnotation в новом интерфейсе AnnotatedElement:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
Это общий метод. Он выводит значение своего параметра T параметра из его аргумента и возвращает соответствующий экземпляр T, как показано следующим фрагментом:
Author a = Othello.class.getAnnotation(Author.class);
До родословных вам пришлось бы придать результат Автору. Также у вас не было бы способа заставить компилятор проверить, что фактический параметр представляет собой подкласс Annotation. [...]
Ну, мне никогда не приходилось использовать такие вещи. Кто-нибудь?
Ответ 4
Я нашел class<T>
полезным при создании запросов к системному реестру. Например.
<T> T getService(Class<T> serviceClass)
{
...
}
Ответ 5
Как указывают другие ответы, есть много и веских причин, почему этот class
был сделан общим. Однако есть много раз, что у вас нет способа узнать тип общего типа для использования с Class<T>
. В этих случаях вы можете просто игнорировать предупреждения о желтом затмении или использовать Class<?>
... Что я делаю это;)
Ответ 6
Следуя ответам @Kire Haglin, еще один пример методов дженериков можно увидеть в документации для JAXB unmarshalling:
public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
throws JAXBException {
String packageName = docClass.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance( packageName );
Unmarshaller u = jc.createUnmarshaller();
JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
return doc.getValue();
}
Это позволяет unmarshal
возвращать документ произвольного типа дерева содержимого JAXB.
Ответ 7
Вы часто хотите использовать подстановочные знаки с Class
. Например, Class<? extends JComponent>
, позволит вам указать, что класс является некоторым подклассом JComponent
. Если вы извлекли экземпляр Class
из Class.forName
, вы можете использовать Class.asSubclass
для выполнения броска перед тем, как попытаться построить экземпляр.
Ответ 8
В Java <T>
означает общий класс. Общий класс - это класс, который может работать с любым типом данных или, другими словами, мы можем сказать, что он не зависит от типа данных.
public class Shape<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Где T означает тип. Теперь, когда вы создаете экземпляр этого класса Shape, вам нужно будет указать компилятору, для какого типа данных он будет работать.
Пример:
Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();
Integer - это тип, а String - также тип.
<T>
специально обозначает универсальный тип. Согласно Java Docs - универсальный тип - это универсальный класс или интерфейс, параметризованный по типам.
Ответ 9
Просто чтобы добавить другой пример, общая версия класса (Class<T>
) позволяет писать общие функции, такие как ниже.
public static <T extends Enum<T>>Optional<T> optionalFromString(
@NotNull Class<T> clazz,
String name
) {
return Optional<T> opt = Optional.ofNullable(name)
.map(String::trim)
.filter(StringUtils::isNotBlank)
.map(String::toUpperCase)
.flatMap(n -> {
try {
return Optional.of(Enum.valueOf(clazz, n));
} catch (Exception e) {
return Optional.empty();
}
});
}
Ответ 10
Это сбивает с толку в начале. Но это помогает в следующих ситуациях:
class SomeAction implements Action {
}
// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction");
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.
Ответ 11
Просто используйте класс говядины:
public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
throws JAXBException {
String packageName = docClass.getPackage().getBeef();
JAXBContext beef = JAXBContext.newInstance( packageName );
Unmarshaller u = beef.createBeef();
JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
return doc.getBeef();
}