2009-06-16 30 views
49

Si estoy creando una clase de Java para ser genéricos, tales como:Java clase genérica - Determinar Tipo

public class Foo<T> 

¿Cómo se puede determinar internamente a esa clase, lo que 'T' terminó siendo?

public ???? Bar() 
{ 
    //if its type 1 
    // do this 
    //if its type 2 
    // do this 
    //if its type 3 
    // do this 
    //if its type 4 
    // do this 
} 

He hurgó la API de Java y jugaba con la materia de reflexión, instanceof, getClass, .class, etc, pero me parece que no puede hacer cara o cruz de ellos. Siento que estoy cerca y solo necesito combinar varias llamadas, pero sigo quedándome corto.

Para ser más específicos, estoy tratando de determinar si la clase fue instanciada con uno de los 3 tipos posibles.

+15

El deseo de hacer esto es casi seguro que sea una mala elección de diseño. Lo más probable es que la declaración if requiera el conocimiento de cada "tipo" para poder implementarse, por lo que probablemente debería tener todas esas clases del mismo tipo padre, y "do this" debería ser un método virtual. Puede implicar envolver clases, pero en general su diseño mejorará si continúa por este camino. –

+2

Las posibilidades en los enunciados if son casos extremos entre todos los posibles tipos de llamadas, que requieren implementaciones completamente separadas. Estoy de acuerdo con lo que estás diciendo aquí, pero la aplicación específica es una piedra y un lugar difícil. –

+1

La necesidad de obtener la clase para un param tipo genérico no siempre es una mala decisión de diseño. Un requisito legítimo (¡mío! :) podría ser una llamada a un método que requiera un parámetro Class <>. – dahvyd

Respuesta

58

He utilizado una solución similar a la que él explica aquí para algunos proyectos y la encontré bastante útil.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

El jist de que está utilizando el siguiente:

public Class returnedClass() { 
    ParameterizedType parameterizedType = (ParameterizedType)getClass() 
               .getGenericSuperclass(); 
    return (Class) parameterizedType.getActualTypeArguments()[0]; 
} 
+0

+1! Iba a escribir sobre esto –

+0

+1 Y esta es prácticamente la solución que obtuve después de seguir investigando, así que la cambié por mi respuesta aceptada. –

+0

+1 excelente! Lo estuve buscando durante horas ... –

12

Debido a type erasure, no hay manera de hacer esto directamente. Lo que podría hacer, sin embargo, es pasar un Class<T> en el constructor y retenerlo dentro de su clase. Luego puede verificarlo contra los tres posibles tipos de Class que permita.

Sin embargo, si sólo hay tres tipos posibles, es posible que desee considerar refactorización en un lugar enum.

+0

Esta parece ser la mejor solución, aunque menos bonita que una verdadera clase genérica azul. Gracias por el aporte. –

+0

Una vez más, le insto a que considere seriamente el polimorfismo en lugar de un enunciado si basado en el tipo. –

+0

Lamentablemente, los elementos en consideración no se relacionan de ninguna manera, por lo que una solución polimórfica no es práctica. Los posibles elementos que se incluirían en la lógica de enunciados if son casos límite de todos los demás procesos "normales" que deben tratarse de manera completamente diferente, de una manera no relacional. –

0

El punto entero de una clase genérica es que usted no necesita saber el tipo que se utiliza ....

+2

Cierto, sin embargo, si diseño una clase que pueda ser instanciada genéricamente para que sea de cualquier tipo, quiero manejar un subconjunto específico de todos los tipos posibles como casos límite con lógica específica. –

+0

Luego crea diferentes subtipos. O delegue en estrategias (o similar). –

3

El problema es que la mayor parte del material genérico desaparecerán durante la compilación.

Una solución común es guardar el tipo durante la creación del objeto.

Para una breve introducción en la conducta Tipo de Erasure java leer este page

+1

Esto es muy útil: he visto soluciones que implican esto y pueden terminar yendo por esta ruta. –

+1

Probablemente significa que su diseño es pobre. –

0

Parece que lo que quiere es, de hecho, no es una clase genérica, sino una interfaz con un número de diferentes implementaciones. Pero tal vez sería más claro si estableciera su objetivo real y concreto.

44

En contraste con .NET, los genéricos de Java se implementan mediante una técnica llamada "borrado de tipos".

Lo que esto significa es que el compilador utilizará la información de tipo cuando se generan los archivos de clase, pero no transferir esta información al código de bytes. Si nos fijamos en las clases compiladas con javap o herramientas similares, se dará cuenta de que un List<String> es un simple List (de Object) en el archivo de clase, al igual que lo fue en pre-Java-5 código.

Código acceder a la lista genérica será "reescrito" por el compilador para incluir los elencos que tendría que escribir a sí mismo en las versiones anteriores. En efecto, los dos fragmentos de código son idénticos desde el punto de vista de código de bytes, una vez que el compilador se hace con ellos:

Java 5:

List<String> stringList = new ArrayList<String>(); 
stringList.add("Hello World"); 
String hw = stringList.get(0); 

Java 1.4 y antes:

List stringList = new ArrayList(); 
stringList.add("Hello World"); 
String hw = (String)stringList.get(0); 

Al leer los valores de una clase genérica en Java 5 se insertan automáticamente para el parámetro de tipo declarado. Al insertar, el compilador verificará el valor que intenta colocar y abortará con un error si no es un String.

Todo se hizo para mantener interoperables las bibliotecas antiguas y el nuevo código genérico sin necesidad de volver a compilar las librerías existentes. Esta es una gran ventaja sobre el modo .NET donde las clases genéricas y las no genéricas conviven una al lado de la otra, pero no se pueden intercambiar libremente.

Ambos enfoques tienen sus pros y sus contras, pero así es en Java.

Para volver a su pregunta original: No podrá obtener la información de tipo en tiempo de ejecución, porque simplemente ya no está allí, una vez que el compilador ha hecho su trabajo. Esto seguramente es una limitación en algunos aspectos y existen algunas formas de mal humor que generalmente se basan en almacenar una instancia de clase en algún lugar, pero esta no es una función estándar.

+2

+1 para una amplia información y contraste con C# - gracias –

+1

Gracias. Lo siento, no hay un botón +10. –

0

Estoy de acuerdo con Visage. Generics es para validación en tiempo de compilación, no para tipeo dinámico en tiempo de ejecución. Parece que lo que necesitas es realmente solo el patrón de fábrica. Pero si tu "haz esto" no es creación de instancias, entonces un Enum simple también funcionará igual de bien. Al igual que Michael dijo, si tienes un ejemplo un poco más concreto, obtendrás mejores respuestas.

+0

Tengo que estar en desacuerdo aquí, y ha habido varias menciones de que una solución no polimórfica es necesaria en el espacio problemático específico, y estoy muy contento con las respuestas que he recibido. –

1

Si conoce algunos tipos específicos que son significativos, debe crear subclases de su tipo genérico con la implementación.

Así

public class Foo<T> 

public ???? Bar() 
{ 
    //else condition goes here 
} 

Y luego

public class DateFoo extends Foo<Date> 

public ???? Bar() 
{ 
    //Whatever you would have put in if(T == Date) would go here. 
} 
Cuestiones relacionadas