Comment utiliser la classe <T> en Java?


247

Il y a une bonne discussion sur les génériques et ce qu'ils font vraiment dans les coulisses de cette question , donc nous savons tous que Vector<int[]>c'est un vecteur de tableaux entiers, et HashTable<String, Person>est un tableau dont les clés sont des chaînes et des valeurs Persons. Cependant, ce qui me décourage, c'est l'utilisation deClass<> .

La classe java Classest censée prendre également un nom de modèle (ou du moins, le soulignement jaune dans eclipse me le dit). Je ne comprends pas ce que je devrais y mettre. L'intérêt de l' Classobjet est lorsque vous ne disposez pas pleinement des informations sur un objet, pour la réflexion et autres. Pourquoi cela me fait-il spécifier quelle classe l' Classobjet contiendra? Je ne sais clairement pas, ou je n'utiliserais pas l' Classobjet, j'utiliserais celui spécifique.

Réponses:


136

L'utilisation de la version générée de la classe Class vous permet, entre autres, d'écrire des choses comme

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

et puis vous pouvez être sûr que l'objet Class que vous recevez s'étend Collection, et une instance de cette classe sera (au moins) une Collection.


183

Tout ce que nous savons, c'est que « toutes les instances d'une classe quelconque partagent le même objet java.lang.Class de ce type de classe »

par exemple)

Student a = new Student();
Student b = new Student();

ensuite a.getClass() == b.getClass() c'est vrai.

Supposons maintenant

Teacher t = new Teacher();

sans génériques, ce qui suit est possible.

Class studentClassRef = t.getClass();

Mais c'est faux maintenant ..?

par exemple) public void printStudentClassInfo(Class studentClassRef) {}peut être appelé avecTeacher.class

Cela peut être évité en utilisant des génériques.

Class<Student> studentClassRef = t.getClass(); //Compilation error.

Maintenant, qu'est-ce que T ?? T est des paramètres de type (également appelés variables de type); délimité par des crochets (<>), suit le nom de la classe.
T est juste un symbole, comme un nom de variable (peut être n'importe quel nom) déclaré lors de l'écriture du fichier de classe. Plus tard, T sera remplacé par
un nom de classe valide lors de l'initialisation (HashMap<String> map = new HashMap<String>(); )

par exemple) class name<T1, T2, ..., Tn>

Class<T>Représente donc un objet de classe de type de classe spécifique 'T '.

Supposons que vos méthodes de classe doivent fonctionner avec des paramètres de type inconnus comme ci-dessous

/**
 * 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; }
}

Ici, T peut être utilisé comme Stringtype CarName

OU T peut être utilisé comme Integertype comme modelNumber ,

OU T peut être utilisé comme Objecttype d' instance de voiture valide .

Maintenant, voici le POJO simple qui peut être utilisé différemment au moment de l'exécution.
Collections, par exemple) List, Set, Hashmap sont les meilleurs exemples qui fonctionneront avec différents objets selon la déclaration de T, mais une fois que nous avons déclaré T comme chaîne,
par exemple)HashMap<String> map = new HashMap<String>(); Ensuite, il n'acceptera que les objets d'instance String Class.

Méthodes génériques

Les méthodes génériques sont des méthodes qui introduisent leurs propres paramètres de type. Cela revient à déclarer un type générique, mais la portée du paramètre type est limitée à la méthode où il est déclaré. Les méthodes génériques statiques et non statiques sont autorisées, ainsi que les constructeurs de classes génériques.

La syntaxe d'une méthode générique inclut un paramètre de type, entre crochets, et apparaît avant le type de retour de la méthode. Pour les méthodes génériques, la section des paramètres de type doit apparaître avant le type de retour de la méthode.

 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;
}

Voici <K, V, Z, Y>la déclaration des types utilisés dans les arguments de méthode qui devraient avant le type de retour qui estboolean ici.

Dans le ci-dessous; la déclaration de type <T>n'est pas requise au niveau de la méthode, car elle est déjà déclarée au niveau de la classe.

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

Mais ci-dessous est faux car les paramètres de type au niveau de la classe K, V, Z et Y ne peuvent pas être utilisés dans un contexte statique (méthode statique ici).

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

D'AUTRES SCÉNARIOS VALIDES SONT

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;
        }
    }

Et enfin, la méthode statique a toujours besoin d'une <T>déclaration explicite ; Il ne dérivera pas du niveau de la classe Class<T>. Cela est dû au fait que le niveau de classe T est lié à l'instance.

Lisez aussi Restrictions sur les génériques

Caractères génériques et sous-typage

argument de type pour une méthode générique


2
Ma réponse sur les wildCards bornés stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar

"La classe <T> représente un objet de classe de type de classe spécifique" T "." Cela a du sens. Merci ..
bynu022

Cette réponse est terriblement déroutante dans la façon dont elle utilise les classes (de l'école) dans une question sur les classes (en Java). Il est difficile de savoir de quoi parle l'auteur d'une phrase à l'autre.
Echox

@Echox Je suis désolé, je peux l'améliorer, si vous avez des questions spécifiques.
Kanagavelu Sugumar


32

De la documentation Java:

[...] Plus surprenant, la classe Class a été générée. Les littéraux de classe fonctionnent désormais comme des jetons de type, fournissant à la fois des informations de type à l'exécution et à la compilation. Cela permet un style d'usines statiques illustré par la méthode getAnnotation dans la nouvelle interface AnnotatedElement:

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

Il s'agit d'une méthode générique. Il déduit la valeur de son paramètre de type T de son argument et renvoie une instance appropriée de T, comme illustré par l'extrait de code suivant:

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

Avant les génériques, vous auriez dû convertir le résultat en auteur. De plus, vous n'auriez eu aucun moyen de faire vérifier au compilateur que le paramètre réel représentait une sous-classe d'Annotation. [...]

Eh bien, je n'ai jamais eu à utiliser ce genre de choses. N'importe qui?


2
Je pensais l'avoir fait. Un cadre (en quelque sorte) avec lequel j'ai travaillé vous demandait de transmettre le nom de classe des services dont dépendait votre module. J'ai construit un calque au-dessus de celui qui a pris des objets de classe, afin de limiter la quantité de choix. En utilisant la Class<? extends X>notation, j'ai pensé que je pouvais la limiter aux types de «service» uniquement. Sauf qu'il n'y avait pas de type de «service» commun, donc je ne pouvais le faire qu'avec Class<?>. Hélas.
fwielstra

9

J'ai trouvé class<T>utile lorsque je crée des recherches de registre de service. Par exemple

<T> T getService(Class<T> serviceClass)
{
    ...
}

5

Comme d'autres réponses le soulignent, il existe de nombreuses et bonnes raisons pour lesquelles cela a classété rendu générique. Cependant, il y a de nombreuses fois où vous n'avez aucun moyen de connaître le type générique à utiliser Class<T>. Dans ces cas, vous pouvez simplement ignorer les avertissements d'éclipse jaune ou vous pouvez utiliser Class<?>... C'est comme ça que je le fais;)


@SuppressWarnings("unchecked")vient à la rescousse! (Faites juste attention à toujours appliquer à la plus petite portée d'un possible car il fait des problèmes potentiels obscurs dans votre code.)
Donal Fellows

4

Suite à la réponse de @Kire Haglin, un autre exemple de méthodes génériques peut être vu dans le documentation pour 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();
}

Cela permet unmarshalde renvoyer un document d'un type d'arbre de contenu JAXB arbitraire.


2

Vous souhaitez souvent utiliser des caractères génériques avec Class. Par exemple, Class<? extends JComponent>vous permettrait de spécifier que la classe est une sous-classe de JComponent. Si vous avez récupéré l' Classinstance à partir de Class.forName, vous pouvez utiliser Class.asSubclasspour effectuer le transtypage avant d'essayer, par exemple, de construire une instance.


2

En java <T>signifie classe générique. Une classe générique est une classe qui peut fonctionner sur n'importe quel type de type de données ou en d'autres termes, nous pouvons dire qu'elle est indépendante du type de données.

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 signifie type. Maintenant, lorsque vous créez une instance de cette classe Shape, vous devrez indiquer au compilateur pour quel type de données cela fonctionnera.

Exemple:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

Un entier est un type et une chaîne est également un type.

<T>représente spécifiquement le type générique. Selon Java Docs - Un type générique est une classe ou une interface générique qui est paramétrée sur des types.


0

Juste pour jeter un autre exemple, la version générique de Class ( Class<T>) permet d'écrire des fonctions génériques telles que celle ci-dessous.

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

-2

C'est déroutant au début. Mais cela aide dans les situations ci-dessous:

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.

4
N'est-ce pas juste une façon incroyablement compliquée de dire Action a = new Action ()?
Karl

1
Action a = new Action() ? Actioni une interface, c'est que SomeActionnous essayons d'obtenir une instance de. Nous n'avons que le nom de SomeActiondisponible au moment de l'exécution.
fastcodejava

Cela ne passe pas le contrôle de typage: le compilateur java n'a aucun moyen de dire que <code> Class.forName ("SomeAction") </code> sera de type <code>Class<Action> </code>, car cela ne fera que être connu au moment de l'exécution.
tonio

@tonio, c'est vrai, donc vous devrez probablement envelopper cette première ligne dans une sorte de try / catch. Mais en supposant qu'aucune exception n'y soit levée, la deuxième ligne est garantie de fonctionner.
MatrixFrog

2
Ce que vous décrivez vraiment, c'est que SomeAction.class correspond au modèle Class <? étend Action> - c'est-à-dire que si vous avez une méthode useAction (Class <? étend Action> klass), vous pouvez appeler useAction (SomeAction.class).
Thomas Andrews

-5

Utilisez simplement la classe de boeuf:

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

4
Cela ne fournit pas vraiment une réponse complète à la question. Si vous pensez que vous avez quelque chose à ajouter, modifiez cette réponse.
MasterAM
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.