¿Cómo funciona la inferencia de tipos de JDK 8 con genéricos?

Tengo un código que no se pudo compilar con JDK 7 pero logró compilar con JDK 8.

Para resumir el código real:

interface A {
...
}

class B implements A {
...
}

public void AAA(List<A> list) {...}

AAA(Collections.singletonList(new B()));

Collections.singletonList se define como

public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

Por lo que yo sé basado en genérico, T se inferirá a B, por lo que Collections.singletonList (new B ()) será List que no se puede asignar a List ya que Java generic es invariante.

Pero con JDK 8, T se infiere a A y la compilación tiene éxito.

Me gustaría saber cómo se infiere T a A, ya que aquí hay dos variables para el tipo T: A y B.

¿Hay orden de prioridad? ¿O el compilador encuentra una clase de ancestro común?

Adjuntar documento oficial es más apreciado!

¡Gracias por adelantado!

ps1. La versión de JDK 7 es Oracle 1.7.0_79 y la versión de JDK 8 es Oracle 1.8.0_66.

ps2. Aquí están los enlaces para el código real:

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/examples/storm-opentsdb-examples/src/main/java/org/apache/storm/opentsdb/SampleOpenTsdb.tol.

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/OpenTsdbBolt.java

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/TupleOpenTsperbDatapointMp.

Respuesta 1

Bueno, hay un nuevo capítulo completo , §18. Inferencia de tipos , en la especificación del lenguaje, pero no es una lectura fácil. Incluso el resumen de la primera sección, que aborda exactamente su pregunta, es difícil:

En comparación con la edición Java SE 7 de The Java® Language Specification, los cambios importantes en la inferencia incluyen:

  • Agregar soporte para expresiones lambda y referencias de métodos como argumentos de invocación de métodos.
  • Generalizando para definir la inferencia en términos de expresiones poli, que pueden no tener tipos bien definidos hasta que se complete la inferencia. Esto tiene el notable efecto de mejorar la inferencia para el método genérico anidado y las invocaciones de constructores de diamantes.
  • Describiendo cómo se usa la inferencia para manejar los tipos de destino de la interfaz funcional parametrizada con comodines y el análisis de métodos más específico.
  • Aclarando la distinción entre la prueba de aplicabilidad de invocación (que involucra solo los argumentos de invocación) y la inferencia de tipo de invocación (que incorpora un tipo de destino).
  • Retrasar la resolución de todas las variables de inferencia, incluso aquellas con límites inferiores, hasta la inferencia de tipo de invocación, para obtener mejores resultados.
  • Mejora del comportamiento de inferencia para variables interdependientes (o independientes).
  • Eliminar errores y posibles fuentes de confusión. Esta revisión maneja de manera más cuidadosa y precisa la distinción entre contextos de conversión específicos y subtipos, y describe la reducción paralelamente a las relaciones de no inferencia correspondientes. Cuando hay desviaciones intencionales de las relaciones de no inferencia, éstas se identifican explícitamente como tales.
  • Sentar las bases para la evolución futura: las mejoras o nuevas aplicaciones de inferencia serán más fáciles de integrar en la especificación.

La segunda viñeta tiene el mayor impacto en su ejemplo de código. Tiene una invocación de método anidado de un método genérico sin especificar argumentos de tipo explícito, lo que lo convierte en una llamada expresión poli cuyo tipo real puede inferirse del tipo de destino , que es el tipo de parámetro AAAen su caso.

Entonces, esta es una constelación bastante fácil, ya AAAque no es genérica y no tiene ambigüedad con respecto a su tipo de parámetro. Es siempre List<A>. Aquí no se busca una "clase ancestral común", todo lo que se debe verificar es si el tipo de expresión de argumento ( B) es compatible con el tipo inferido ( A).

Respuesta: 2

Esto realmente se explica aquí al final en Tipos de destino .

El tipo de destino se ha ampliado para incluir el argumento del método ...

Lo que esto significa es que

 AAA(Collections.singletonList(new B())); // returns List<A> NOT List<B>

En el método jdk-7, el argumento no se usa para averiguar el tipo de destino, es por eso que T se infiere a B y es por eso que falla.

Respuesta: 3

Estoy aprendiendo Java e intento el ejemplo de Stack. Este es el código: import java.util. *; pila de clase pública Try {static boolean checkParity (expresión de cadena, cadena abierta, cadena ...

Estoy usando Spring MVC 4.1.4. Tengo algunas configuraciones globales para compartir en toda la aplicación. Estas configuraciones solo deberían cargarse cuando inicie el servidor. Sé que puedo usar context-param <context-param> ...

Actualmente estoy escribiendo una Prueba de Unidad para el Patrón de Diseño de Estrategia. Estoy comparando la salida del sistema con una cadena en el método ClaimEquals. La salida se ve igual pero mi prueba sigue fallando ... Soy ...

Estoy usando el registro de commons en una aplicación Java, y quiero registrar la excepción del seguimiento de la pila. catch (excepción de excepción) {logger.error ("FailedCreateActivityFunction Exception ...