java - Genéricos de comodín inferidos en el tipo de retorno
generics bounded-wildcard (1)
El motivo por el que funciona el constructor es que está especificando explícitamente los parámetros de tipo. El método estático también funcionará si haces eso:
Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello");
Por supuesto, la razón principal por la que tiene un método estático en primer lugar es probablemente solo para obtener la inferencia de tipo (que no funciona con los constructores).
El problema aquí (como sugirió) es que el compilador está realizando una conversión de captura . Creo que esto es como resultado de [§15.12.2.6 del JLS] :
- El tipo de resultado del método elegido se determina de la siguiente manera:
- Si el método que se invoca se declara con un tipo de devolución de vacío, el resultado es nulo.
- De lo contrario, si la conversión sin marcar era necesaria para que el método fuera aplicable, entonces el tipo de resultado es el borrado (§4.6) del tipo de retorno declarado del método.
- De lo contrario, si el método que se invoca es genérico, entonces para 1in, sea Fi los parámetros de tipo formal del método, sea Ai los argumentos de tipo reales inferidos para la invocación del método, y sea R el tipo de retorno declarado del método invocado El tipo de resultado se obtiene aplicando la conversión de captura (§5.1.10) a R [F1: = A1, ..., Fn: = An].
- De lo contrario, el tipo de resultado se obtiene aplicando la conversión de captura (§5.1.10) al tipo dado en la declaración del método.
Si realmente desea la inferencia, una posible solución es hacer algo como esto:
Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello");
El pair
variables tendrá un tipo más amplio, y significa un poco más de escritura en el nombre del tipo de la variable, pero al menos ya no es necesario convertir el método en la llamada.
Java puede a menudo inferir genéricos basados en los argumentos (e incluso en el tipo de retorno, en contraste con, por ejemplo, C #).
Caso en cuestión: tengo una clase genérica Pair<T1, T2>
que solo almacena un par de valores y se puede usar de la siguiente manera:
Pair<String, String> pair = Pair.of("Hello", "World");
El método of
ve así:
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
Muy agradable. Sin embargo, esto ya no funciona para el siguiente caso de uso, que requiere comodines:
Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello");
(Observe la List.class
explícita para hacer que List.class
el tipo correcto).
El código falla con el siguiente error (proporcionado por Eclipse):
No coincide el tipo: no se puede convertir de
TestClass.Pair<Class<capture#1-of ?>,String>
aTestClass.Pair<Class<?>,String>
Sin embargo, llamar explícitamente al constructor sigue funcionando como se esperaba:
Pair<Class<?>, String> pair =
new Pair<Class<?>, String>((Class<?>) List.class, "hello");
¿Alguien puede explicar este comportamiento? ¿Es por diseño? Se quiere ? ¿Estoy haciendo algo mal o tropecé con una falla en el diseño / error en el compilador?
Suposición salvaje: la "captura # 1-de?" De alguna manera parece implicar que el compilador sobre la marcha rellena el comodín, lo que hace que el tipo sea una Class<List>
, y por lo tanto no se realiza la conversión (de Pair<Class<?>, String>
para Pair<Class<List>, String>
). ¿Es esto correcto? ¿Hay una manera de evitar esto?
Para completar, aquí hay una versión simplificada de la clase Pair
:
public final class Pair<T1, T2> {
public final T1 first;
public final T2 second;
public Pair(T1 first, T2 second) {
this.first = first;
this.second = second;
}
public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
return new Pair<T1, T2>(first, second);
}
}