web-dev-qa-db-fra.com

Comment utiliser la classe <T> en Java?

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[]> est un vecteur de tableaux d'entiers, et HashTable<String, Person> est un tableau dont les clés sont des chaînes et des valeurs Persons. Cependant, ce qui me stoppe, c’est l’utilisation de Class<>.

La classe Java Class est également supposée prendre un nom de modèle (ou le soulignement jaune dans Eclipse me dit quoi que ce soit. Je ne comprends pas ce que je devrais y mettre. L’intérêt de l’objet Class est quand vous n’avez pas toutes les informations sur un objet, pour une réflexion, etc. Pourquoi est-ce que cela me fait spécifier quelle classe l'objet Class tiendra? De toute évidence, je ne sais pas ou je n'utiliserais pas l'objet Class, mais plutôt l'objet spécifique.

206
Karl

Utilisation de la version générée de la classe Classe vous permet, entre autres choses, d’écrire des choses comme

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

et alors 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.

115
Yuval

Tout ce que nous savons, c’est "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();

Alors a.getClass() == b.getClass() est vrai.

Maintenant assumer

Teacher t = new Teacher();

sans génériques le ci-dessous est possible.

Class studentClassRef = t.getClass();

Mais c'est faux maintenant ..?

par exemple, public void printStudentClassInfo(Class studentClassRef) {} peut être appelé avec Teacher.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 
nom de classe valide lors de l'initialisation (HashMap<String> map = new HashMap<String>();)

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

Donc, Class<T> représente un objet de classe du type de classe spécifique 'T'.

Supposons que vos méthodes de classe doivent fonctionner avec des paramètres de type inconnu 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 type String comme CarName

OR T peut être utilisé comme type Integer comme modelNumber,

OR T peut être utilisé en tant que type Object en tant que instance de voiture valide

Maintenant, voici ci-dessus le simple POJO qui peut être utilisé différemment à l'exécution .
Collections e.g) List, Set, Hashmap sont les meilleurs exemples qui fonctionneront avec différents objets conformément à la déclaration de T, mais une fois que nous avons déclaré T en tant que String
p. ex. HashMap<String> map = new HashMap<String>(); Il n'acceptera alors que les objets d'instance de la classe String.

Méthodes génériques

Les méthodes génériques sont des méthodes qui introduisent leurs propres paramètres de type. Ceci est similaire à la déclaration d'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 classe 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 résultat de la méthode. Pour les méthodes génériques, la section de paramètre 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;
}

Ici <K, V, Z, Y> est la déclaration des types utilisés dans les arguments de la méthode devant précéder le type de retour qui est ici boolean.

Dans le 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 le paramètre ci-dessous est incorrect, car les paramètres de type classe, K, V, Z et Y ne peuvent pas être utilisés dans un contexte statique (méthode statique dans ce cas).

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

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 de la déclaration explicite <T>; Il ne proviendra pas du niveau de classe Class<T>. Ceci est dû au fait que le niveau de classe T est lié à l'instance. 

Lire aussi Restrictions on Generics

148
Kanagavelu Sugumar

De la documentation Java:

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

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

Ceci est 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û attribuer le résultat à Author. De plus, vous n'auriez eu aucun moyen de faire vérifier par le 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?

30
raupach

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

<T> T getService(Class<T> serviceClass)
{
    ...
}
8
Kire Haglin

Comme le soulignent d’autres réponses, il existe de nombreuses et bonnes raisons pour lesquelles cette class a été rendue générique. Cependant, il arrive souvent que vous n'ayez aucun moyen de connaître le type générique à utiliser avec Class<T>. Dans ces cas, vous pouvez simplement ignorer les avertissements Eclipse jaunes ou vous pouvez utiliser Class<?> ... C'est comme ça que je le fais;)

5
bruno conde

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

Cela permet à unmarshal de renvoyer un document d'un type d'arborescence de contenu JAXB arbitraire.

3
Stew

Dans Java <T> signifie classe générique. Une classe générique est une classe qui peut fonctionner sur tout 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 devez indiquer au compilateur le type de données sur lequel il travaillera.

Exemple:

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

Entier est un type et String est également un type.

<T> signifie 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 les types.

2
Priyesh Diukar

Vous voulez 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'instance Class à partir de Class.forName, vous pouvez utiliser Class.asSubclass pour effectuer la conversion avant de tenter, par exemple, de créer une instance.

2

Pour vous donner un autre exemple, la version générique de Class (Class<T>) permet d’écrire des fonctions génériques comme 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();
                }
            });
}
0
zeronone