valores sentencia retornar retornan que metodos genericos java generics java-8

sentencia - return metodos en java



¿Por qué se borra el tipo genérico de devolución cuando hay una conversión no verificada de un parámetro de método en Java 8? (4)

Echemos un vistazo a la especificación del lenguaje Java:

Java 8

15.12.2.6. Tipo de Invocación de Método

...

  • Si el método elegido no es genérico, entonces:

    • Si la conversión sin marcar era necesaria para que el método fuera aplicable, los tipos de parámetros del tipo de invocación son los tipos de parámetros del tipo de método, y el tipo de retorno y los tipos lanzados vienen dados por los borrados del tipo de retorno y los tipos de lanzamiento de los métodos. tipo.

    ...

Java 7

15.12.2.6. Método de resultados y tipos de tiros

El tipo de resultado del método elegido se determina de la siguiente manera:

  • Si el método elegido se declara con un tipo de retorno de vacío, entonces 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.

Es importante entender, qué significa "método genérico":

Reference

8.4.4. Métodos genéricos

Un método es genérico si declara una o más variables de tipo (§4.4).

En otras palabras, el método

static Producer<String> getProducer(List<Integer> list)

tiene parámetros genéricos y tipos de retorno, pero no es genérico, ya que no declara variables de tipo.

Por lo tanto, las partes citadas se aplican y, a pesar de hacer diferencias con respecto a los requisitos previos, concuerdan con las consecuencias de esta invocación específica del método, " si la conversión no verificada era necesaria para que el método fuera aplicable, entonces el tipo de resultado es la eliminación ... de los métodos declarados tipo de devolución ".

Así que es el compilador más antiguo que viola la especificación utilizando el tipo de retorno Producer<String> lugar del Producer borrado.

Considere el siguiente ejemplo de código:

import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List list = new ArrayList<Integer>(); String response = getProducer(list).get(); } static Producer<String> getProducer(List<Integer> list) { return new Producer<String>(); } } class Producer<T> { T get() { return null; } }

Cuando se compila en Java 7, solo produce una advertencia esperada para getProducer(list) :

Advertencia: (7, 39) java: se requiere conversión sin java.util.List<java.lang.Integer> : java.util.List<java.lang.Integer> found: java.util.List

Sin embargo, cuando se compila en Java 8, produce el siguiente error para la response = getProducer(list).get() :

Error: (7, 48) java: tipos incompatibles: java.lang.Object no se puede convertir a java.lang.String

Entonces, aparentemente, el tipo devuelto por getProducer(list) no es Producer<String> , sino que se borró Producer (que también se confirma mediante la función ''extract variable'' en el IDE). Esto es muy desconcertante porque el método getProducer siempre devuelve Producer<String> .

Curiosamente, podría solucionarse evitando la conversión sin getProducer mientras se llama getProducer método getProducer , ya sea por:

  • Cambie el tipo de parámetro de getProducer de List<Integer> a List
  • Cambiar el tipo de variable de List de List a List<Integer>

Actualizaciones

  • Java utilizado es Oracle JDK 1.8.0_40
  • También intenté usar las opciones de origen y destino de 1.5 a 1.7 con el compilador de Java 8 y el resultado fue el mismo.

Preguntas

  • ¿Cómo podría el tipo genérico del argumento pasado afectar un tipo genérico del valor de retorno del método mientras que el tipo genérico del valor de retorno está fijado en la firma del método?
  • ¿Por qué hay un cambio de comportamiento incompatible con versiones anteriores entre Java 7 y Java 8?

Esto parece un problema de compatibilidad conocido reportado here y here .

Desde el segundo enlace:

El siguiente código compilado, con advertencias, en JDK 7 no se compilará en JDK 8:

import java.util.List; class SampleClass { static class Baz<T> { public static List<Baz<Object>> sampleMethod(Baz<Object> param) { return null; } } private static void bar(Baz arg) { Baz element = Baz.sampleMethod(arg).get(0); } }

Compilar este código en JDK 8 produce el siguiente error:

SampleClass.java:12: error: tipos incompatibles: el objeto no se puede convertir a Baz

Baz element = Baz.sampleMethod(arg).get(0);

Nota: SampleClass.java usa operaciones no verificadas o inseguras. Nota: Recompile con -Xlint: sin marcar para más detalles. 1 error

Derivado de esto, el código de OP puede ser reparado reemplazando esta línea (la declaración de tipo en el lado derecho me echó, la leo como una lista de matriz tipada que no es):

List list = new ArrayList<Integer>();

con

List<Integer> list = new ArrayList<Integer>();

que no dará lugar a que el tipo se borre del tipo de devolución del método getProducer(List<Integer> list)

Citar de nuevo el segundo enlace:

En este ejemplo, se está pasando un tipo sin procesar al sampleMethod(Baz<Object>) que se puede aplicar mediante subtipos (consulte JLS, Java SE 7 Edition, sección 15.12.2.2). Una conversión sin marcar es necesaria para que el método sea aplicable, por lo que su tipo de retorno se borra (consulte JLS, Java SE 7 Edition, sección 15.12.2.6). En este caso, el tipo de retorno de sampleMethod(Baz<Object>) es java.util.List lugar de java.util.List<Baz<Object>> y, por lo tanto, el tipo de retorno de get(int) es Object , que no es una asignación -compatible con Baz .


La verdadera pregunta es; ¿Por qué puedes usar el tipo sin procesar? Por compatibilidad con versiones anteriores. Si es por compatibilidad con versiones anteriores, la suposición es que solo se deben usar tipos crudos.

¿Cómo podría afectar el tipo genérico de argumento pasado al tipo de valor de retorno del método mientras que el tipo de valor de retorno genérico se corrige en la firma del método?

Un método o constructor tiene uno de los dos modos. Es completamente genérico o está completamente tipeado para compatibilidad con versiones anteriores. No hay un modo de modo de uso en el que se tipea parcialmente en bruto y en parte es genérico. La única razón por la que los tipos primarios son una opción es por compatibilidad con versiones anteriores y en esta situación se supone que todos los tipos son / fueron tipos crudos.

¿Por qué hay un cambio de comportamiento tan incompatible hacia atrás entre Java7 y Java8?

Ya que Java 1.4 ya no es compatible y no lo ha sido por un tiempo, el argumento de compatibilidad con versiones anteriores no es tan fuerte y tiene algún sentido que los tipos sin formato tengan un lugar en el lenguaje actual.


Supongo que el razonamiento es el siguiente, que se centra en la compatibilidad con la "migración".

Si la invocación proporciona un argumento de tipo sin procesar a un parámetro de método de un tipo genérico, es muy probable que el código de invocación sea el código pre-genérico, y la declaración de método, que también se usaba antes del genérico, ha sido "evolucionado" para usar tipos genéricos.

Para mantener una compatibilidad perfecta, la solución infalible es borrar el tipo de método, de modo que sea como la versión pre-genérica. De esta forma, el significado de la invocación permanece exactamente igual.

Una solución más sofisticada podría ser demasiado complicada para JLS, por ejemplo, ¿cómo podemos hacer la inferencia de tipos si hay argumentos en bruto?

Hoy en día, esa suposición ya no es válida; la invocación es más probable que sea un código post-genérico, que, por diversas razones, todavía utiliza el tipo sin formato. Es mejor "corregir" el tipo crudo desde el principio

List list0 = ...; // got it from somewhere, possibly from an old API @SuppressWarnings("unchecked") List<Integer> list = list0;