with type parameter method generic java generics static-methods

java - type - Método estático en una clase genérica?



method with generic (11)

En Java, me gustaría tener algo como:

class Clazz<T> { static void doIt(T object) { // shake that booty } }

Pero yo obtengo

Cannot make a static reference to the non-static type T

No entiendo los genéricos más allá de los usos básicos y por lo tanto no puedo tener mucho sentido de eso. No ayuda el hecho de que no pude encontrar mucha información en Internet sobre el tema.

¿Podría alguien aclarar si tal uso es posible, de una manera similar? Además, ¿por qué mi intento original no tuvo éxito?


@BD en Rivenhill: Dado que esta vieja pregunta ha recibido una atención renovada el año pasado, sigamos un poco, solo por el bien de la discusión. El cuerpo de su método doIt no hace nada T -específico en absoluto. Aquí está:

public class Clazz<T> { static <T> void doIt(T object) { System.out.println("shake that booty ''" + object.getClass().toString() + "'' !!!"); } // ... }

Así que puedes descartar completamente todas las variables de tipo y solo codificar

public class Clazz { static void doIt(Object object) { System.out.println("shake that booty ''" + object.getClass().toString() + "'' !!!"); } // ... }

De acuerdo. Pero volvamos más cerca del problema original. La primera variable de tipo en la declaración de clase es redundante. Solo se necesita el segundo sobre el método. Aquí vamos de nuevo, pero aún no es la respuesta final:

public class Clazz { static <T extends Saying> void doIt(T object) { System.out.println("shake that booty "+ object.say()); } public static void main(String args[]) { Clazz.doIt(new KC()); Clazz.doIt(new SunshineBand()); } } // Output: // KC // Sunshine interface Saying { public String say(); } class KC implements Saying { public String say() { return "KC"; } } class SunshineBand implements Saying { public String say() { return "Sunshine"; } }

Sin embargo, todo es demasiado alboroto por nada, ya que la siguiente versión funciona de la misma manera. Todo lo que necesita es el tipo de interfaz en el parámetro de método. No hay variables de tipo a la vista en ningún lado. ¿Ese era realmente el problema original?

public class Clazz { static void doIt(Saying object) { System.out.println("shake that booty "+ object.say()); } public static void main(String args[]) { Clazz.doIt(new KC()); Clazz.doIt(new SunshineBand()); } } interface Saying { public String say(); } class KC implements Saying { public String say() { return "KC"; } } class SunshineBand implements Saying { public String say() { return "Sunshine"; } }


Algo como lo siguiente te acercaría

class Clazz { public static <U extends Clazz> void doIt(U thing) { } }

EDITAR: ejemplo actualizado con más detalles

public abstract class Thingo { public static <U extends Thingo> void doIt(U p_thingo) { p_thingo.thing(); } protected abstract void thing(); } class SubThingoOne extends Thingo { @Override protected void thing() { System.out.println("SubThingoOne"); } } class SubThingoTwo extends Thingo { @Override protected void thing() { System.out.println("SuThingoTwo"); } } public class ThingoTest { @Test public void test() { Thingo t1 = new SubThingoOne(); Thingo t2 = new SubThingoTwo(); Thingo.doIt(t1); Thingo.doIt(t2); // compile error --> Thingo.doIt(new Object()); } }


Creo que esta sintaxis no se ha mencionado aún (en el caso de que desee un método sin argumentos):

class Clazz { static <T> T doIt() { // shake that booty } }

Y la llamada:

String str = Clazz.<String>doIt();

Espero que esto ayude a alguien.


Cuando especifica un tipo genérico para su clase, JVM sabe que solo tiene una instancia de su clase, no definición. Cada definición tiene solo tipo parametrizado.

Los genéricos funcionan como plantillas en C ++, por lo que primero debe crear una instancia de su clase y luego usar la función con el tipo especificado.


Es posible hacer lo que quiera usando la sintaxis para métodos genéricos al declarar su método doIt() (observe la adición de <T> entre static y void en la firma del método de doIt() ):

class Clazz<T> { static <T> void doIt(T object) { // shake that booty } }

Conseguí que el editor de Eclipse aceptara el código anterior sin el. Cannot make a static reference to the non-static type T error de Cannot make a static reference to the non-static type T y luego lo expandí al siguiente programa de trabajo (completo con una referencia cultural apropiada para la edad):

public class Clazz<T> { static <T> void doIt(T object) { System.out.println("shake that booty ''" + object.getClass().toString() + "'' !!!"); } private static class KC { } private static class SunshineBand { } public static void main(String args[]) { KC kc = new KC(); SunshineBand sunshineBand = new SunshineBand(); Clazz.doIt(kc); Clazz.doIt(sunshineBand); } }

Que imprime estas líneas en la consola cuando lo ejecuto:

agitar esa clase de booty com.eclipseoptions.datamanager.Clazz $ KC ''!!!
agitar esa clase de botín com.eclipseoptions.datamanager.Clazz $ SunshineBand ''!!!


Java no sabe qué es T hasta que crea una instancia de un tipo.

Tal vez puedas ejecutar métodos estáticos llamando a Clazz<T>.doit(something) pero parece que no puedes.

La otra forma de manejar las cosas es poner el parámetro tipo en el método mismo:

static <U> void doIt(U object)

que no te da la restricción correcta para U, pero es mejor que nada ...


Me encontré con este mismo problema. Encontré mi respuesta descargando el código fuente de Collections.sort en el marco java. La respuesta que utilicé fue poner el genárico en el método, no en la definición de la clase.

Entonces esto funcionó:

public class QuickSortArray { public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){ //do it } }

Por supuesto, después de leer las respuestas anteriores, me di cuenta de que esta sería una alternativa aceptable sin usar una clase genárica:

public static void quickSort(Comparable[] array, int bottom, int top){ //do it }


No puede usar los parámetros de tipo genérico de una clase en métodos estáticos o campos estáticos. Los parámetros de tipo de la clase solo tienen alcance, por ejemplo, los métodos y los campos de instancia. Para campos estáticos y métodos estáticos, se comparten entre todas las instancias de la clase, incluso instancias de diferentes parámetros de tipo, por lo que obviamente no pueden depender de un parámetro de tipo particular.

No parece que su problema requiera usar el parámetro de tipo de la clase. Si describe con más detalle lo que intenta hacer, tal vez podamos ayudarlo a encontrar una forma mejor de hacerlo.


Otros ya han respondido su pregunta, pero además puedo recomendar detalladamente el libro genérico O''Reilly Java . Es un tema sutil y complejo a veces, y si a menudo parece tener restricciones sin sentido, pero el libro hace un buen trabajo al explicar por qué los genéricos de java son como son.


Se menciona correctamente en el error: no se puede hacer una referencia estática al tipo no estático T. La razón es que el parámetro de tipo T puede ser reemplazado por cualquiera de los argumentos de tipo, por ejemplo, Clazz<String> o Clazz<integer> etc. Pero los campos / métodos estáticos son compartidos por todos los objetos no estáticos de la clase.

El siguiente fragmento está tomado del doc :

El campo estático de una clase es una variable de nivel de clase compartida por todos los objetos no estáticos de la clase. Por lo tanto, los campos estáticos de los parámetros de tipo no están permitidos. Considera la siguiente clase:

public class MobileDevice<T> { private static T os; // ... }

Si se permitieran campos estáticos de parámetros de tipo, entonces se confundiría el siguiente código:

MobileDevice<Smartphone> phone = new MobileDevice<>(); MobileDevice<Pager> pager = new MobileDevice<>(); MobileDevice<TabletPC> pc = new MobileDevice<>();

Debido a que el campo estático del sistema operativo se comparte por teléfono, buscapersonas y PC, ¿cuál es el tipo de sistema operativo actual? No puede ser Smartphone, Buscapersonas y TabletPC al mismo tiempo. Por lo tanto, no puede crear campos estáticos de parámetros de tipo.

Como Chris correctamente señaló en su share , necesita usar el parámetro tipo con el método y no con la clase en este caso. Puedes escribirlo así:

static <E> void doIt(E object)


También para decirlo en términos simples, sucede debido a la propiedad "Borrar" de los genéricos. Lo que significa que aunque definimos ArrayList<Integer> y ArrayList<String> , en el momento de la compilación permanece como dos tipos de concreto diferentes pero en el tiempo de ejecución, la JVM borra los tipos genéricos y crea solo una clase ArrayList en lugar de dos clases. Entonces, cuando definimos un método de tipo estático o cualquier otro para un genérico, es compartido por todas las instancias de ese genérico, en mi ejemplo, es compartido por ArrayList<Integer> y ArrayList<String> Es por eso que obtiene el error. ¡El parámetro de tipo genérico de una clase no está permitido en un contexto estático!