java - programacion - Combinando tipos crudos y métodos genéricos
objeto t en java (5)
Aquí hay una pregunta, este primer listado de código compila perfectamente (JDK 1.6 | JDK 1.7):
ArrayList<String> a = new ArrayList<String>();
String[] s = a.toArray(new String[0]);
Sin embargo, si declaro la referencia de la List
como un tipo sin formato:
ArrayList a = new ArrayList();
String[] s = a.toArray(new String[0]);
Recibo un error del compilador que dice que se requiere la String[]
pero se encontró el Object[]
.
Esto significa que mi compilador está interpretando el método genérico como un Object[]
devuelto Object[]
pesar de recibir un String[]
como su argumento.
toArray(myArray)
firma del método toArray(myArray)
:
<T> T[] toArray(T[] a);
Por lo tanto, es un método parametrizado cuyo parámetro de tipo <T>
no tiene relación alguna con el de la Lista (es decir, <E>
).
No tengo idea de cómo el uso de un tipo en bruto aquí afecta la evaluación de los métodos parametrizados usando parámetros de tipo independiente.
- ¿Alguien tiene alguna idea de por qué este código no se compila?
- ¿Alguien sabe alguna referencia donde este comportamiento está documentado?
Cuando no usa genéricos, el compilador lo trata como un tipo sin procesar y, por lo tanto, cada tipo genérico se convierte en Object
y, por lo tanto, no puede pasar la String[]
porque necesita el Object[]
Así que aquí está el trato - si usas
List l = new ArrayList<String>();
Está utilizando el tipo sin formato y todos sus miembros de instancia se reemplazan por sus contrapartes de borrado. En particular, cada tipo parametrizado que aparece en una declaración de método de instancia se reemplaza con su contraparte en bruto. Ver JLS 4.8 para más detalles.
Esta es la descripción más cercana que encontré en la especificación para describir este comportamiento observado:
El tipo de un constructor (§8.8), el método de instancia (§8.8, §9.4), o el campo no estático (§8.3) M de un tipo sin formato C que no se hereda de sus superclases o superinterfaces es el borrado de su tipo en la declaración genérica correspondiente a C. El tipo de un miembro estático de un tipo sin procesar C es el mismo que su tipo en la declaración genérica correspondiente a C.
Es un error en tiempo de compilación pasar los parámetros de tipo reales a un miembro de tipo no estático de un tipo en bruto que no se hereda de sus superclases o superinterfaces.
Según lo anterior y el comportamiento observado, creo que es seguro decir que todos los tipos de parámetros genéricos se eliminan de un tipo sin formato. Por supuesto, el uso de los tipos en bruto se desaconseja en el código no heredado:
El uso de tipos brutos se permite solo como una concesión a la compatibilidad de código heredado. El uso de tipos en bruto en el código escrito después de la introducción de la genérico en el lenguaje de programación Java está totalmente desaconsejado. Es posible que las versiones futuras del lenguaje de programación Java no permitan el uso de tipos brutos .
No es exactamente lo que usted esperaría, pero si se refiere a una clase genérica en forma bruta, pierde la capacidad de usar genéricos de alguna manera para los miembros de la instancia. Tampoco está restringido a métodos genéricos, mira esto:
public class MyContainer<T> {
public List<String> strings() {
return Arrays.asList("a", "b");
}
}
MyContainer container = new MyContainer<Integer>();
List<String> strings = container.strings(); //gives unchecked warning!
Esta es la parte relevante del JLS ( 4.8 ):
El tipo de un constructor (§8.8), método de instancia (§8.4, §9.4), o campo no estático (§8.3) M de un tipo sin formato C que no se hereda de sus superclases o superinterfaces es el tipo sin formato que corresponde al borrado de su tipo en la declaración genérica correspondiente a C.
No puede haber ningún parámetro de tipo pasado al método toArray () ya que su ArrayList no es una lista parametrizada, solo sabe que contiene Objetos, eso es todo. a.toArray()
siempre devolverá una matriz Object []. De nuevo, debe convertirlo en (String[])
(con todos los peligros en él) si desea especificar que contiene el tipo de Cadena específico.
Puede ser interesante que este comportamiento pueda ser "resuelto". Utilice dos interfaces, una interfaz base no genérica y una interfaz genérica. Entonces el compilador sabe que todas las funciones de la interfaz no genérica de base no son genéricas y las tratarán así.
Este comportamiento es muy molesto si se usan flujos y encadenamiento de funciones y, por lo tanto, lo resuelvo como sigue.
Soution a través de la herencia de la interfaz :
public interface GenericInterface<X extends Y> extends BaseInterface
{
X getValue();
}
public interface BaseInterface
{
String getStringValue();
}
Ahora puedes hacerlo siguiendo sin avisar ni problemas:
GenericInterface object = ...;
String stringValue = object.getStringValue();