2012-03-20 12 views
5

Mi problema se puede resumir en marcha por este fragmento:Genéricos y convertir al tipo de derecho

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A { 
    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); // Won't compile! 

     // However, I know that the target can be passed to the 
     // method safely because its type matches. 
    } 
} 

Mi clase A utiliza una instancia de TheClass con su tipo genéricos desconocido. Presenta un método con un objetivo pasado como Object ya que la instancia TheClass se puede parametrizar con cualquier clase. Sin embargo, el compilador no me permitirá pasar el objetivo así, lo cual es normal.

¿Qué debo hacer para evitar este problema?

Una solución sucia es declarar la instancia como TheClass<? super Object>, que funciona bien, pero es semánticamente mal ...

Otra solución he usado antes de que se declare la instancia como tipo de prima, simplemente TheClass, pero es malo practicar, entonces quiero corregir mi error.

Solución

public class A { 
    private TheClass<Object> instance; // type enforced here 

    public A(TheClass<?> instance) { 
     this.instance = (TheClass<Object>) instance; // cast works fine 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); 
    } 
} 
+2

¿Por qué no se escribe también 'A'? –

+0

+1, para una buena pregunta !! esperando las respuestas ... – aProgrammer

+0

A es parte de una biblioteca de código abierto que hice, que es utilizada por mucha gente, y escribir A significaría una gran pausa para la API. Recientemente descubrí este problema porque antes usaba tipos sin formato para la instancia "TheClass", ya que no sabía que era una mala concepción. Funcionó bien, pero se considera una mala práctica, así que quiero corregir este mal uso. –

Respuesta

4
public class A { 
    private TheClass<Object> instance; 

    public A(TheClass<Object> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

o

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void do(T target) { 
     instance.theMethod(target); 
    } 
} 
+0

+1. Esto es justo lo que estaba escribiendo; ¡pero llegaste primero! –

+0

Tu primera idea me envió a la solución, ¡gracias! En realidad, hago cumplir el tipo de objeto en la declaración del campo de instancia, como tú. Sin embargo, sigo permitiendo el comodín en el parámetro constructor, pero transfiero esta instancia al tipo forzado. Esto solo tiene un efecto en el tiempo de compilación, y no requerirá nada de mis usuarios (ver la última edición de la pregunta). –

1

La solución es también escriba A. El uso de un comodín ? hace que pierda la información de tipo de TheClass y no hay forma de recuperarlo más tarde. Hay algunos trucos feos que podría hacer, pero su mejor tiro es para que escriban A:

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void doIt(T target) { 
     instance.theMethod(target); 
    } 
} 

No va a romper cualquier API tampoco.

+0

+1, gracias. Escribir A no es una posibilidad para mí, ya que agregaría complejidad inútil a la biblioteca, pero tiene razón, esa fue una de las pocas soluciones. –

1

La razón para el error de compilación es que el ? comodín indica la desconocida tipo en Java. Puede declarar una variable con un parámetro genérico desconocido, pero no puede instanciar uno con él. Lo que significa que en su constructor la clase genérica pasada en podría haber sido creada para contener tipos que son incompatibles con lo que está intentando usar más adelante. Caso de ejemplo:

public class A { 
    public static void main(String[] args) { 
     TheClass<String> stringHolder = null; // should constrain parameters to strings 
     A a = new A(stringHolder); 
     a.donot(Float.valueOf(13)) ; // this is an example of what could happen 
    } 

    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

En este caso, el compilador le impide escribir código que hubiera sido propenso a errores. Como han señalado otros, debe agregar un tipo de parámetro genérico a su clase A, para restringir los tipos permitidos, lo que eliminará el error de tiempo de compilación.

Algunos de lectura sugerida: Oracle Generics Trail

+0

Gracias. Aprendí la teoría de los genéricos en detalles profundos durante los últimos días para resolver este problema, así que entiendo sus mecanismos. No habría estado en tal situación si supiera la teoría antes de usar tipos crudos por todas partes. Ahora que ya está hecho, estaba tratando de eliminar tipos crudos, pero encontré este problema. –

+0

De todos modos, su código fallará en tiempo de ejecución, lanzando una excepción y deteniendo el programa. Está bien, es como una afirmación implícita y el error mostrará a los usuarios que hay un error en su código. No será un error de tiempo de compilación, pero sigue siendo un error y no habrá un comportamiento indefinido, solo una falla reciente y limpia con debug stacktrace :) +1 para su ejemplo, sin embargo, ayudará a las personas a comprender por qué compilar- la verificación del tiempo es importante. –

+0

@ AurélienRibon - El código que publiqué es solo para fines ilustrativos. Cambié el método 'do' a' donot', pero eso es lo que corregí errores. – Perception

Cuestiones relacionadas