¿Cómo utilizar Class<T> en Java?

Resuelto Karl asked hace 15 años • 11 respuestas

Hay una buena discusión sobre los genéricos y lo que realmente hacen detrás de escena en esta pregunta , por lo que todos sabemos que Vector<int[]>es un vector de matrices de enteros y HashTable<String, Person>es una tabla cuyas claves son cadenas y valores Person. Sin embargo, lo que me desconcierta es el uso de Class<>.

Se supone que la clase java Classtambién debe tomar un nombre de plantilla (o eso me dice el subrayado amarillo en eclipse). No entiendo qué debo poner ahí. El objetivo del Classobjeto es cuando no tienes toda la información sobre un objeto, para la reflexión y demás. ¿Por qué me hace especificar qué clase Classcontendrá el objeto? Claramente no lo sé, o no estaría usando el Classobjeto, usaría el específico.

Karl avatar Jan 21 '09 00:01 Karl
Aceptado

Todo lo que sabemos es que " todas las instancias de cualquier clase comparten el mismo objeto java.lang.Class de ese tipo de clase " .

p.ej)

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

Entonces a.getClass() == b.getClass()es verdad.

Ahora supongamos

Teacher t = new Teacher();

sin genéricos lo siguiente es posible.

Class studentClassRef = t.getClass();

¿Pero esto está mal ahora...?

por ejemplo) public void printStudentClassInfo(Class studentClassRef) {}se puede llamar conTeacher.class

Esto se puede evitar utilizando genéricos.

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

Ahora que es T?? T son parámetros de tipo (también llamados variables de tipo); delimitado por corchetes angulares (<>), sigue al nombre de la clase.
T es solo un símbolo, como un nombre de variable (puede ser cualquier nombre) declarado durante la escritura del archivo de clase. Posteriormente, esa T será sustituida por
un nombre de clase válido durante la inicialización ( HashMap<String> map = new HashMap<String>();)

p.ej)class name<T1, T2, ..., Tn>

Entonces Class<T>representa un objeto de clase de tipo de clase específico ' T'.

Supongamos que los métodos de su clase tienen que funcionar con parámetros de tipo desconocido como se muestra a continuación

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

Aquí T se puede utilizar como Stringtipo CarName

O T se puede utilizar como Integertipo como modelNumber ,

O T se puede utilizar como Objecttipo como instancia de automóvil válida .

Ahora bien, lo anterior es el POJO simple que se puede usar de manera diferente en tiempo de ejecución.
Colecciones, por ejemplo) List, Set, Hashmap son los mejores ejemplos que funcionarán con diferentes objetos según la declaración de T, pero una vez que declaramos T como String, por
ejemplo) HashMap<String> map = new HashMap<String>();, solo aceptará objetos de instancia de String Class.

Métodos genéricos

Los métodos genéricos son métodos que introducen sus propios parámetros de tipo. Esto es similar a declarar un tipo genérico, pero el alcance del parámetro de tipo se limita al método donde se declara. Se permiten métodos genéricos estáticos y no estáticos, así como constructores de clases genéricos.

La sintaxis de un método genérico incluye un parámetro de tipo, entre corchetes angulares, y aparece antes del tipo de retorno del método. Para métodos genéricos, la sección de parámetro de tipo debe aparecer antes del tipo de retorno del método.

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

Aquí <K, V, Z, Y>está la declaración de tipos utilizados en los argumentos del método que deberían estar antes del tipo de retorno que está booleanaquí.

A continuación; La declaración de tipo <T>no es necesaria a nivel de método, ya que ya está declarada a nivel de clase.

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

Pero lo siguiente es incorrecto ya que los parámetros de tipo de nivel de clase K, V, Z e Y no se pueden usar en un contexto estático (método estático aquí).

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

OTROS ESCENARIOS VÁLIDOS SON

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

Y, finalmente, el método estático siempre necesita <T>una declaración explícita; No derivará del nivel de clase Class<T>. Esto se debe a que el nivel de clase T está vinculado con la instancia.

Lea también Restricciones a los genéricos

Comodines y subtipos

argumento de tipo para un método genérico

Kanagavelu Sugumar avatar Oct 23 '2013 14:10 Kanagavelu Sugumar

Usar la versión genificada de class Class le permite, entre otras cosas, escribir cosas como

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

y luego puede estar seguro de que el objeto Clase que recibe se extiende Collectiony una instancia de esta clase será (al menos) una Colección.

Yuval avatar Jan 20 '2009 17:01 Yuval