2010-12-16 6 views
11

Supongo que, desde el momento de la compilación, así como desde el aspecto del tiempo de ejecución, no sería un problema para .getClass() proporcionar un valor de retorno correctamente tipado.genéricos de Java: ¿por qué someObject.getClass() no devuelve Class <? extends T>?

Pero debo estar equivocado.

public class _GetClassGenerics2 { 

    static class MyClass { 
    } 

    public static void main(String[] args) { 
    MyClass myInstance = new MyClass(); 
    // here it works 
    Class<? extends MyClass> type = myInstance.getClass(); 

    myMethod(myInstance); 
    } 

    public static <T extends MyClass> void myMethod(T instance) { 
    Class<? extends T> type = instance.getClass(); 
// java.lang.RuntimeException: Uncompilable source code - incompatible types 
// required: java.lang.Class<? extends T> 
// found: java.lang.Class<capture#1 of ? extends _GetClassGenerics2.MyClass> 
    } 

} 

EDIT: no funciona con Class<T> y Class<? super T> tampoco.

+0

¿Qué tal 'Clase type'? – sjngm

+0

Encontré una pregunta que podría explicarlo: http://stackoverflow.com/questions/252055/java-generics-wildcards – sjngm

+0

'@ sjngm': 1) Ver edición, 2) La otra pregunta dada no es relevante, I (usualmente) entiende la diferencia entre 'super' y' extends'. –

Respuesta

4

Según the Javadoc of the getClass method:

El tipo de resultado real es Class<? extends |X|> donde | X | es la borradura del tipo estático de la expresión en la que se llama getClass.Para ejemplo, no se requiere un molde en este fragmento de código

En este caso, el valor de |X| en el fragmento de código es MyClass, por lo tanto, instance.getClass() es asignable a solamente Class<? extends MyClass> o Class<?>.

La razón de esta redacción específica es porque cuando se dice que para esta variable que tiene tipo T, donde <T extends MyClass>, puede haber varias clases que se extienden MyClass y por lo tanto capaces de satisfacer los criterios T extends MyClass. Sin información de tiempo de ejecución no hay forma de saber qué subclase de implementación concreta de MyClass se aprobó en el método. Por lo tanto, para proporcionar una solución genérica, devuelve <? extends MyClass> ya que eso sería válido para cualquier subclase de MyClass independientemente de qué instancia de clase se pase.

+3

Lo que no entiendo es por qué X es MyClass, no T. –

+1

@Sergey Tachenov: Debido a que AFAICT, '| X |' es un borrado y, por lo tanto, no puede ser un tipo parametrizado. Si fuera simplemente 'T', el Javadoc no habría mencionado explícitamente * borrado de tipo estático *. –

+3

Oh, finalmente lo tengo. La especificación de lenguaje 4.6 dice explícitamente que "La eliminación de una variable de tipo (§4.4) es la eliminación de su límite más a la izquierda". ¿Cuál es MyClass en nuestro caso? Aunque no veo por qué lo hicieron de esta manera. –

0

Java no admite un tipo genérico de < esto> p.

objeto podría implementar

class Object { 
    Class<this> getClass() 
} 

Pero no hay manera de getClass() para expresar que le proporcione un tipo que es la clase del objeto. El compilador tampoco tiene conocimientos nativos de lo que hace este método.

En mi humilde opinión, Este comportamiento debería haber sido admitido.

+0

Buena respuesta. Sin embargo, tenga en cuenta que explica el segundo paso antes del primero. El problema inmediato es que Object.getClass() no devuelve el tipo "correcto". Usted explica * por qué * no hace esto. – sleske

+0

¿Podría señalar algún recurso que explique qué es exactamente ''? No puedo encontrarlo en la especificación del lenguaje y no puedo compilar algo así. –

+0

Lo sentimos, Java ** does ** admite la función "this-type"? Sería genial, pero supongo que te refieres a ** no **, ¿verdad? Porque 'Clase getSomething()' no funcionaría. –

0

En lugar de

Class<? extends T> type = instance.getClass(); 

es necesario utilizar

Class<? extends MyClass> type = instance.getClass(); 

No se puede utilizar directamente el T aquí.

El motivo es la firma del método Object.getClass() (que está invocando). Es:

public final Class<? extends Object> getClass() 

Así que están tratando de convertir de Class<? extends Object> a Class<? extends T>, que no está permitido (ya que se han reducido de fundición). Se es permitido el uso de una conversión explícita:

Class<? extends T> type = (Class<? extends T>) instance.getClass(); 

va a funcionar (aunque se genera un aviso de seguridad de tipo).

+0

Esto no será un problema, pero la pregunta es por qué. ¿Hay una razón? En este momento, sabemos que la instancia debe ser de clase T (cualquiera que sea) o de su subclase. Entonces, ¿por qué? extiende T' no está permitido? –

+0

@Sergey: La razón es que, aunque sabemos que 'instancia' debe ser de tipo' T' o subclase, 'Object.getClass()' no tiene eso en cuenta. Ver mi respuesta editada. – sleske

+1

Todavía no lo entiendo. El javadoc dice "El tipo de resultado real es Clase donde | X | es la eliminación del tipo estático de la expresión a la que se llama getClass". En este caso X es T. ¿Estás tratando de decir que X es en realidad un Objeto? Y eso es porque es un parámetro de tipo (ya que funciona fuera del método genérico)? –

6

java.lang.Class no representa un tipo (use java.lang.reflect.Type para eso). Si T, digamos ArrayList<String>, entonces no tiene sentido que haya un Class<ArrayList<String>>.

Vale la pena señalar que en este caso particular no es necesario que el método sea genérico.

public static <T extends MyClass> void myMethod(T instance) { 

es equivalente a:

public static void myMethod(MyClass instance) { 
Cuestiones relacionadas