Как использовать Class <T> в Java?

247

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

Предполагается, что java-класс также Classдолжен принимать имя шаблона (или, как мне подсказывает жёлтое подчеркивание в затмении). Я не понимаю, что я должен положить туда. Весь смысл Classобъекта в том, что у вас нет полной информации об объекте, для размышлений и тому подобного. Почему это заставляет меня указывать, какой класс Classбудет содержать объект? Я точно не знаю, иначе я бы не использовал Classобъект, я бы использовал конкретный.

Карл
источник

Ответы:

136

Использование обобщенной версии класса Class позволяет, помимо прочего, писать такие вещи, как

Class<? extends Collection> someCollectionClass = someMethod();

и тогда вы можете быть уверены, что получаемый вами объект Class расширяется Collection, и экземпляр этого класса будет (как минимум) коллекцией.

Юваль
источник
183

Все, что мы знаем: « Все экземпляры любого класса используют один и тот же объект 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

ИЛИ T можно использовать как Integerтип в качестве modelNumber ,

ИЛИ T можно использовать как Objectтип в качестве действительного экземпляра автомобиля .

Теперь выше приведен простой POJO, который можно использовать по-разному во время выполнения.
Коллекции, например) List, Set, Hashmap - лучшие примеры, которые будут работать с различными объектами согласно объявлению T, но как только мы объявили T как String,
например). 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 связан с экземпляром.

Также прочитайте Ограничения на Дженерики

Подстановочные знаки и подтипы

введите аргумент для универсального метода

Канагавелу Сугамар
источник
2
Мой ответ об ограниченных wildCards stackoverflow.com/questions/1368166/…
Канагавелу Сугамар
«Класс <T> представляет объект класса определенного типа класса« T ».», Который имеет смысл. Спасибо ..
bynu022
Этот ответ ужасно запутан тем, как он использует классы (из школы) в вопросе о классах (на Java). Трудно понять, о чем говорит автор, из одного предложения в другое.
Echox
@Echox Извините, я могу улучшить его, если у вас есть какие-то конкретные вопросы.
Канагавелу Сугамар
1
stackoverflow.com/questions/4829631/…
Канагавелу Сугамар
32

Из документации Java:

[...] Что еще более удивительно, класс Class был обобщен. Литералы классов теперь функционируют как токены типов, предоставляя информацию как во время выполнения, так и во время компиляции. Это включает стиль статических фабрик, примером которого является метод getAnnotation в новом интерфейсе AnnotatedElement:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

Это общий метод. Он выводит значение параметра типа T из своего аргумента и возвращает соответствующий экземпляр T, как показано в следующем фрагменте:

Author a = Othello.class.getAnnotation(Author.class);

До генериков вам пришлось бы привести результат к автору. Также у вас не было бы способа заставить компилятор проверить, что фактический параметр представляет подкласс Annotation. [...]

Ну, мне никогда не приходилось использовать такие вещи. Кто угодно?

Raupach
источник
2
Я (думал, что я) сделал. Фреймворк (своего рода), с которым я работал, требовал, чтобы вы передавали имя класса служб, от которых зависел ваш модуль. Я построил слой поверх того, который принимал объекты класса, чтобы ограничить количество вариантов. Используя Class<? extends X>обозначение, которое я понял, я могу ограничить его только типами «сервис». За исключением того, что не было общего типа «сервис», поэтому я мог сделать это только с Class<?>. Увы.
fwielstra
9

Я нашел class<T>полезным, когда я создаю поиск в реестре сервисов. Например

<T> T getService(Class<T> serviceClass)
{
    ...
}
Кире Хаглин
источник
5

Как указывают другие ответы, есть много веских причин, почему это classбыло сделано обобщенно. Однако есть много раз, когда у вас нет никакого способа узнать универсальный тип для использования Class<T>. В этих случаях вы можете просто игнорировать предупреждения желтого затмения или использовать Class<?>... Вот как я это делаю;)

Бруно Конде
источник
@SuppressWarnings("unchecked")приходит на помощь! (Только будьте осторожны , чтобы всегда применять его как небольшая область , насколько это возможно , как это делает неясные потенциальные проблемы в вашем коде.)
Донал Fellows
4

Следуя ответу @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.

тушеное мясо
источник
2

Вы часто хотите использовать подстановочные знаки с Class. Например, Class<? extends JComponent>позволит вам указать, что класс является некоторым подклассом JComponent. Если вы извлекли Classэкземпляр из Class.forName, то вы можете использовать Class.asSubclassего, прежде чем пытаться, скажем, создать экземпляр.

Том Хотин - Tackline
источник
2

В 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();

Integer - это тип, а String - также тип.

<T>специально обозначает универсальный тип. Согласно Java Docs - универсальный тип - это универсальный класс или интерфейс, параметризованный по типам.

Frostbyte1010
источник
0

Просто добавьте другой пример, универсальная версия 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();
                }
            });
}
zeronone
источник
-2

Это сбивает с толку в начале. Но это помогает в следующих ситуациях:

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.
fastcodejava
источник
4
Разве это не невероятно сложный способ сказать Action a = new Action ()?
Карл
1
Action a = new Action() ? Actionя интерфейс, это SomeActionмы пытаемся получить экземпляр. У нас есть только имя SomeActionдоступно во время выполнения.
fastcodejava
Это не проходит проверку типов: Java-компилятор не может сказать, что <code> Class.forName ("SomeAction") </ code> будет иметь тип <code> Class <Action> </ code>, так как это будет только быть известным во время выполнения.
Тонио
@tonio, верно, так что вам, вероятно, придется заключить эту первую строку в какую-то попытку / поймать. Но при условии, что там нет исключений, вторая строка гарантированно сработает.
MatrixFrog
2
Что вы действительно описываете, так это то, что SomeAction.class соответствует шаблону Class <? extends Action> - то есть, если у вас есть метод useAction (Class <? extends Action> klass), вы можете вызвать useAction (SomeAction.class).
Томас Эндрюс
-5

Просто используйте класс говядины:

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();
}
Yaa
источник
4
Это не дает полного ответа на вопрос. Если вы думаете, что вам есть что добавить, отредактируйте этот ответ.
MasterAM