¿Cómo usar la clase <T> en Java?


247

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 de cuyas claves son cadenas y valores Persons. Sin embargo, lo que me desconcierta es el uso de Class<>.

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

Respuestas:


136

Usar la versión genérica de class Class te 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 Collection, y una instancia de esta clase será (al menos) una Colección.


183

Todo lo que sabemos es " 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 asume

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 usando genéricos.

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

Ahora que es T ?? T es parámetros de tipo (también llamados variables de tipo); delimitado por corchetes angulares (<>), sigue el 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. Más tarde, T será sustituido con
un nombre de clase válido durante la inicialización ( HashMap<String> map = new HashMap<String>();)

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

Por lo tanto, Class<T>representa un objeto de clase de tipo de clase específico ' T'.

Suponga que sus métodos de clase tienen que funcionar con parámetros de tipo desconocidos como 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 puede usarse como Stringtipo como CarName

O T puede usarse como Integertipo como modelNumber ,

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

Ahora aquí lo anterior es el POJO simple que se puede usar de manera diferente en tiempo de ejecución.
Colecciones, por ejemplo) Lista, Conjunto, Mapa de hash son los mejores ejemplos que funcionarán con diferentes objetos según la declaración de T, pero una vez que declaramos T como Cadena,
por ejemplo) HashMap<String> map = new HashMap<String>();Entonces solo aceptará objetos de instancia de Clase de cadena.

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 los métodos genéricos, la sección del 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 los tipos utilizados en los argumentos del método que deberían antes del tipo de retorno que está booleanaquí.

En el de abajo; La declaración de tipo <T>no se requiere a nivel de método, ya que ya se ha declarado a nivel de clase.

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

Pero a continuación 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 una <T>declaración explícita ; No se 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 sobre genéricos

Comodines y subtipos

argumento de tipo para un método genérico


2
Mi respuesta en wildCards acotada stackoverflow.com/questions/1368166/…
Kanagavelu Sugumar

"La clase <T> representa un objeto de clase del tipo de clase específico 'T'" que tiene sentido. Gracias ..
bynu022

Esta respuesta es terriblemente confusa en la forma en que usa las clases (de la escuela) en una pregunta sobre las clases (en Java). Es difícil saber de qué está hablando el autor de una oración a otra.
Echox

@Echox Lo siento, puedo mejorarlo si tienes alguna pregunta específica.
Kanagavelu Sugumar


32

De la documentación de Java:

[...] Más sorprendentemente, la clase Clase ha sido generada. Los literales de clase ahora funcionan como tokens de tipo, proporcionando información de tipo tanto en tiempo de ejecución como en tiempo de compilación. Esto permite un estilo de fábricas estáticas ejemplificadas por el método getAnnotation en la nueva interfaz AnnotatedElement:

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

Este es un método genérico. Infiere el valor de su parámetro de tipo T de su argumento y devuelve una instancia apropiada de T, como se ilustra en el siguiente fragmento:

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

Antes de los genéricos, habría tenido que enviar el resultado al autor. Además, no habría tenido forma de hacer que el compilador verifique que el parámetro real representara una subclase de Anotación. [...]

Bueno, nunca tuve que usar este tipo de cosas. ¿Nadie?


2
Pensé que lo había hecho. Un marco (más o menos) con el que trabajé requería que pasaras el nombre de clase de los servicios de los que dependía tu módulo. Construí una capa encima de eso que tomaba objetos de clase, para restringir la cantidad de opciones. Usando la Class<? extends X>notación, pensé que podría limitarla solo a los tipos de 'servicio'. Excepto que no había un tipo de 'servicio' común, por lo que solo podía hacerlo Class<?>. Pobre de mí.
fwielstra

9

He encontrado class<T>útil cuando creo búsquedas de registro de servicio. P.ej

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

5

Como señalan otras respuestas, hay muchas y buenas razones por las cuales esto classse hizo genérico. Sin embargo, hay muchas veces que no tienes forma de conocer el tipo genérico con el que usar Class<T>. En estos casos, simplemente puede ignorar las advertencias de eclipse amarillo o puede usar Class<?>... Así es como lo hago;)


@SuppressWarnings("unchecked")viene al rescate! (Sólo tenga cuidado de aplicar siempre que se alcance como una pequeña como sea posible, ya que hace posibles problemas oscuros en su código.)
Donal Fellows

4

Siguiendo la respuesta de @Kire Haglin, se puede ver un ejemplo adicional de métodos genéricos en la documentación para la desagregación de 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();
}

Esto permite unmarshaldevolver un documento de un tipo arbitrario de árbol de contenido JAXB.


2

A menudo quieres usar comodines con Class. Por ejemplo, Class<? extends JComponent>le permitiría especificar que la clase es alguna subclase de JComponent. Si ha recuperado la Classinstancia de Class.forName, entonces puede usar Class.asSubclasspara hacer el lanzamiento antes de intentar, por ejemplo, construir una instancia.


2

En java <T>significa clase genérica. Una clase genérica es una clase que puede funcionar en cualquier tipo de tipo de datos o, en otras palabras, podemos decir que es independiente del tipo de datos.

public class Shape<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Donde T significa tipo. Ahora, cuando cree una instancia de esta clase Shape, deberá decirle al compilador en qué tipo de datos estará trabajando.

Ejemplo:

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

Integer es un tipo y String también es un tipo.

<T>específicamente significa tipo genérico. Según Java Docs: un tipo genérico es una clase o interfaz genérica que se parametriza sobre tipos.


0

Solo para agregar otro ejemplo, la versión genérica de Class ( Class<T>) le permite a uno escribir funciones genéricas como la siguiente.

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

Es confuso al principio. Pero ayuda en las siguientes situaciones:

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.

44
¿No es esa una forma increíblemente complicada de decir Acción a = nueva Acción ()?
Karl

1
Action a = new Action() ? Actioni una interfaz, SomeActionestamos tratando de obtener una instancia de. Solo tenemos el nombre de SomeActiondisponible en tiempo de ejecución.
fastcodejava 02 de

Esto no pasa la verificación de tipos: el compilador de Java no tiene forma de decir que <code> Class.forName ("SomeAction") </code> será de tipo <code>Class<Action> </code>, ya que esto solo ser conocido en tiempo de ejecución.
tonio

@tonio, cierto, así que probablemente tengas que ajustar esa primera línea en algún tipo de prueba / captura. Pero suponiendo que no se arroje ninguna excepción, se garantiza que la segunda línea funcione.
MatrixFrog

2
Lo que realmente está describiendo es que SomeAction.class coincide con el patrón Clase <? extiende Acción>, es decir, si tiene un método useAction (Clase <? extiende Acción> klass), puede llamar a useAction (SomeAction.class).
Thomas Andrews

-5

Solo usa la clase de carne de res:

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

44
Esto realmente no proporciona una respuesta completa a la pregunta. Si crees que tienes algo que agregar, edita esta respuesta.
MasterAM
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.