На этот вопрос есть хорошее обсуждение Generics и того, что они действительно делают за кулисами, поэтому все мы знаем, что Vector<int[]> - это вектор целочисленных массивов, а HashTable<String, Person> - это таблица, ключи которой являются строками, а значения Persons.
Однако меня ставит в тупик использование Class<>.
Предполагается, что класс java Class также принимает имя шаблона (по крайней мере, мне так подсказывает желтое подчеркивание в eclipse). Я не понимаю, что мне туда вставить. Вся суть объекта Class в том, что у вас нет полной информации об объекте, для отражения и тому подобного. Почему это заставляет меня указывать, какой класс будет содержать объект Class? Я точно не знаю, иначе я бы не использовал объект Class, я бы использовал конкретный.




Из документации Java:
[...] Что еще более удивительно, класс Class был генерирован. Литералы классов теперь функционируют как токены типов, предоставляя информацию о типах как во время выполнения, так и во время компиляции. Это позволяет использовать стиль статических фабрик, примером которого является метод getAnnotation в новом интерфейсе AnnotatedElement:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
Это общий метод. Он выводит значение своего параметра типа T из своего аргумента и возвращает соответствующий экземпляр T, как показано в следующем фрагменте:
Author a = Othello.class.getAnnotation(Author.class);
До использования дженериков вам приходилось приводить результат к автору. Также у вас не было бы возможности заставить компилятор проверять, что фактический параметр представляет подкласс аннотации. [...]
Ну, мне никогда не приходилось использовать такие вещи. Кто-нибудь?
Использование обобщенной версии класса Class позволяет, среди прочего, писать такие вещи, как
Class<? extends Collection> someCollectionClass = someMethod();
и тогда вы можете быть уверены, что полученный вами объект класса расширяет Collection, и экземпляр этого класса будет (по крайней мере) коллекцией.
Вы часто хотите использовать подстановочные знаки с Class. Например, Class<? extends JComponent> позволит вам указать, что этот класс является подклассом JComponent. Если вы получили экземпляр Class из Class.forName, то вы можете использовать Class.asSubclass для выполнения приведения, прежде чем пытаться, скажем, построить экземпляр.
Как указывают другие ответы, существует много веских причин, по которым этот class был сделан универсальным. Однако во многих случаях у вас нет возможности узнать общий тип для использования с Class<T>. В этих случаях вы можете просто игнорировать желтые предупреждения о затмении или использовать Class<?> ... Вот как я это делаю;)
На помощь приходит @SuppressWarnings("unchecked")! (Просто будьте осторожны и всегда применяйте его к как можно меньшему объему, поскольку делает скрывает потенциальные проблемы в вашем коде.)
Я нашел class<T> полезным при создании запросов в реестре служб. Например.
<T> T getService(Class<T> serviceClass)
{
...
}
Вначале это сбивает с толку. Но это помогает в следующих ситуациях:
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.
Разве это не просто невероятно сложный способ сказать Action a = new Action ()?
Action a = new Action() ? Action - это интерфейс, мы пытаемся получить экземпляр именно SomeAction. У нас есть только имя SomeAction, доступное во время выполнения.
Это не проходит проверку типов: компилятор java не может сказать, что <code> Class.forName ("SomeAction") </code> будет иметь тип <code>Class<Action> </code>, поскольку это будет только быть известным во время выполнения.
@tonio, верно, поэтому вам, вероятно, придется заключить эту первую строку в какую-то попытку / уловить. Но если исключение не возникло, вторая строка гарантированно будет работать.
@MatrixFrog, не совсем: во время выполнения приведение к Class<Action> эквивалентно приведению к Class. Это добавит неявное приведение к Action при использовании экземпляра, но нет статической гарантии, что приведение не завершится ошибкой. Это будет работать только в том случае, если SomeAction является подклассом Action, и во время компиляции java не имеет возможности сказать об этом.
@tonio Я думаю, мы говорим примерно то же самое. Я сказал, что вам нужно обернуть первую строку в try / catch, но (не пытаясь это сделать, так как у меня не открыта моя IDE), я думаю, что на самом деле она должна выглядеть как Class<Action> actionClass = (Class<Action>) Class.forName("SomeAction");, и вы правы, что приведение может потерпеть неудачу. Моя основная мысль заключалась в том, что вторая строка всегда будет успешной, независимо от того, где она вызывается (если actionClass не равен нулю), потому что компилятор не позволит actionClass содержать класс, который не реализует Action.
Класс <SomeAction> не наследуется от Class <Action>. Это просто ошибка, и приведение, хотя и законное, в основном связано с тем, что обобщенные типы являются чем-то вроде запоздалой мысли, и поэтому необходимо разрешить такие приведения. Да, вы можете использовать некоторые функции после этого приведения и получить то, что ожидаете, но другие могут привести к путанице. Неверно, что если X расширяет Y, то Class <X> расширяет Class <Y>.
На самом деле вы описываете, что SomeAction.class соответствует шаблону Class <? extends Action> - то есть, если у вас есть метод useAction (Class <? extends Action> klass), вы можете вызвать useAction (SomeAction.class).
Следуя ответу @Kire Haglin, еще один пример общих методов можно увидеть в документация для демаршаллинга JAXB:
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.
Просто используйте класс говядины:
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();
}
На самом деле это не дает полного ответа на вопрос. Если вы думаете, что вам есть что добавить, отредактируйте этот ответ.
Все, что мы знаем, это "Все экземпляры любого класса используют один и тот же объект 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 - параметры типа (также называемые переменными типа); разделенный угловыми скобками (<>), следует за именем класса.
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
OR T может использоваться как тип Integer как номер модели,
OR T может использоваться как тип Object как действующий экземпляр автомобиля.
Теперь это простой POJO, который можно использовать по-разному во время выполнения.
Collections, например) List, Set, Hashmap - лучшие примеры, которые будут работать с разными объектами в соответствии с объявлением T, но как только мы объявили T как String
e.g) HashMap<String> map = new HashMap<String>(); Тогда он будет принимать только объекты экземпляра класса String.
Общие методы
Универсальные методы - это методы, которые вводят свои собственные параметры типа. Это похоже на объявление универсального типа, но область действия параметра типа ограничена методом, в котором он объявлен. Разрешены статические и нестатические универсальные методы, а также конструкторы универсальных классов.
Синтаксис универсального метода включает параметр типа, заключенный в угловые скобки, и появляется перед типом, возвращаемым методом. Для универсальных методов раздел параметров типа должен располагаться перед типом, возвращаемым методом.
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 связан с экземпляром.
Также читайте Ограничения на дженерики
Подстановочные знаки и подтипы
аргумент типа для универсального метода
Мой ответ о ограниченных подстановочных знаках stackoverflow.com/questions/1368166/…
«Class <T> представляет объект класса определенного типа класса 'T'.», Что имеет смысл. Спасибо..
Этот ответ ужасно сбивает с толку тем, как он использует классы (из школы) в вопросе о классах (в Java). Трудно понять, о чем автор говорит от одного предложения к другому.
@Echox Извините, я могу улучшить его, если у вас есть какие-то конкретные вопросы.
@Echox Может дело в схеме именования? Кто-нибудь еще заметил иронию в использовании типов «Учитель» и «Ученик» для объяснения ссылок на классы? LMAO
Чтобы привести еще один пример, общая версия Class (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();
}
});
}
В 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; }
}
Где Т означает тип. Теперь, когда вы создаете экземпляр этого класса Shape, вам нужно будет сообщить компилятору, для какого типа данных он будет работать.
Пример:
Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();
Целое число - это тип, и Нить также является типом.
<T> означает общий тип. Согласно Java Docs - универсальный тип - это общий класс или интерфейс, который является параметризованный над типами.
Я (думал, что да) сделал. Фреймворк (своего рода), с которым я работал, требовал, чтобы вы передавали имя класса служб, от которых зависел ваш модуль. Я построил слой поверх этого, который взял объекты класса, чтобы ограничить количество вариантов выбора. Используя нотацию
Class<? extends X>, я решил, что могу ограничить ее только «служебными» типами. За исключением того, что не было общего типа «сервис», поэтому я мог делать это только сClass<?>. Увы.