java - significado - Los compiladores se comportan de manera diferente con un parámetro nulo de un método genérico
que es un interprete en programacion (3)
El siguiente código se compila perfectamente con Eclipse, pero no se puede compilar con javac:
public class HowBizarre {
public static <P extends Number, T extends P> void doIt(P value) {
}
public static void main(String[] args) {
doIt(null);
}
}
Simplifiqué el código, por lo que T no se usa en absoluto ahora. Aún así, no veo una razón para el error. Por algún motivo, javac decide que T representa Object, y luego se queja de que Object no se ajusta a los límites de T (que es verdadero):
HowBizarre.java:6: tipos incompatibles; argumento (s) inferido (s) java.lang.Number, java.lang.Object no se ajustan a los límites de tipo variable (s) P, T
encontrado:
<P,T>
vacíorequerido: vacío
doIt(null); ^
Tenga en cuenta que si reemplazo el parámetro nulo con un valor no nulo, compila bien.
¿Cuál de los compiladores se comporta correctamente y por qué? ¿Es esto un error de uno de ellos?
Es más bien un error en javac. Eclipse infiere el tipo correcto.
Puede doIt((Number) null);
llamando doIt((Number) null);
Incluso si no planea usar javac para el desarrollo, solucione este problema, porque las herramientas como Horm o Horven lo usan y causará problemas en caso de que los presente en algún momento.
de la investigación de los polileneófilos, el javac del sol es aparentemente fiel a la especificación. en el pasado también usé otros compiladores y cuando hay un conflicto, siempre resultó que el javac del sol es correcto. Sun tiene la ventaja de documentar su experiencia desde la implementación en la especificación, mientras que los otros chicos tienen que leer la especificación desde cero: es realmente difícil no quedarse dormido cuando la lee.
El problema se debe a una especificación JLS que exige que los argumentos de tipo que de otro modo serían inferibles se deduzcan como Object
, incluso si no cumple los límites (y, en consecuencia, desencadenaría un error de compilación).
El siguiente es un extracto del informe "error" (que se ha anotado adicionalmente para mayor claridad):
"Error" ID 6299211 - variable de tipo de método: inferencia rota por nulo
Este programa no compila:
public class Try { void m() { java.util.Collections.max(null); } }
Estado : CERRADO, NO UN DEFECTO.
Evaluación : ESTO NO ES UN ERROR . El algoritmo de inferencia no puede recopilar ninguna información del argumento (
null
) y el método no se llama en un lugar donde existan expectativas sobre el valor devuelto. En tales casos, el compilador debe inferirjava.lang.Object
para la variable de tipo.JLS 15.12.2.8 Inferencia de argumentos de tipo no resueltos
Cualquier variable de tipo restante que aún no se haya inferido se deduce que tiene el tipo
Object
Sin embargo,
Object
no es un subtipo deComparable<? super Object>
Comparable<? super Object>
y por lo tanto no dentro de los límites de la variable de tipo en la declaración deCollections.max
:
<T extends
Object & Comparable<? super T>
Object & Comparable<? super T>
> T max(Collection<? extends T>)
Exploraciones adicionales
El uso de parámetros de tipo explícito "corrige" el problema:
HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac
Para mostrar que esto tiene menos que ver con un argumento null
y más relacionado con la falta absoluta de información para la inferencia de tipo, puede probar, por ejemplo, cualquiera de las siguientes declaraciones:
<T,U extends Comparable<T>> void doIt()
<T extends Number,U extends T> void doIt()
En cualquier caso, una invocación doIt();
no compila en javac
, ya que debe inferir que U
es un Object
acuerdo con 15.12.2.8, incluso si al hacerlo se desencadenaría un error de compilación.
Nota sobre Eclipse
Si bien ninguno de los fragmentos recogidos arriba compila en alguna versión de javac
, todos lo hacen en alguna versión de Eclipse. Esto sugeriría un error por parte de Eclipse. Se sabe que hay desacuerdos entre los diferentes compiladores.