wildcards useful type method generic data are java generics compiler-errors java-8 type-inference

useful - Inferencia de tipo Java: la referencia es ambigua en Java 8, pero no en Java 7



type erasure java (2)

Bueno, no es como si Java7 estuviera feliz de ejecutarlo. Da un par de advertencias antes de dar el error:

jatin@jatin-~$ javac -Xlint:unchecked -source 1.7 com/company/Main.java warning: [options] bootstrap class path not set in conjunction with -source 1.7 com/company/Main.java:19: error: no suitable method found for set(Derived,Base) set(new Derived(), new Consumer().get()); ^ method Consumer.set(Base,Derived) is not applicable (argument mismatch; Base cannot be converted to Derived) method Consumer.set(Derived,Collection<? extends Consumer>) is not applicable (argument mismatch; Base cannot be converted to Collection<? extends Consumer>) com/company/Main.java:28: warning: [unchecked] unchecked cast return (T) new Derived(); ^ required: T found: Derived where T is a type-variable: T extends Base declared in method <T>get()

El problema es este:

set(new Derived(), new Consumer().get());

Cuando hacemos un new Consumer().get() , si miramos la firma de get . Nos devuelve un Tipo T Sabemos que T alguna manera extiende Base . Pero no sabemos qué es T específicamente. Puede ser cualquier cosa. Entonces, si no podemos decidir específicamente qué es T , entonces, ¿cómo podemos compilar?

Una forma de decirle al compilador es codificarlo y decirlo específicamente por: set(new Derived(), new Consumer().<Derived>get()); .

La razón anterior es extremadamente extremadamente (repetida intencionalmente) peligrosa, es cuando intentas hacer esto:

class NewDerived extends Base { public String getName(){return "name";}; } NewDerived d = new Consumer().<NewDerived>get(); System.out.println(d.getName());

En Java7 (o cualquier versión de Java), lanzará una excepción en tiempo de ejecución:

Excepción en el hilo "main" java.lang.ClassCastException: com.company.Derived no se puede enviar a com.company.NewDerived

Debido a que get devuelve un objeto de tipo Derived pero usted ha mencionado al compilador que es de NewDerived . Y no puede convertir Derived a NewDerived correctamente. Es por eso que muestra una advertencia.

Según el error, ahora entendemos qué está mal con el new Consumer().get() . Su tipo es something that extends base . Haciendo set(new Derived(), new Consumer().get()); busca un método que tome argumentos como Derived (or any super class of it), something that extends Base .

Ahora ambos métodos califican para el primer argumento. Según el segundo argumento, something that extends base , nuevamente ambos son elegibles, ya que algo puede ser Derivado o extendido Derivado o extendido Colección. Es por eso que Java8 arroja Error.

Según Java7, su inferencia de tipos es un poco más débil. Entonces trata de hacer algo similar,

Base base = new Consumer().get(); set(new Derived(), base);

Y nuevamente, no puede encontrar el método correcto que tome Derived, Base como argumentos. Por lo tanto, arroja un error, pero por diferentes razones:

set(new Derived(), new Consumer().get()); ^ method Consumer.set(Base,Derived) is not applicable (argument mismatch; Base cannot be converted to Derived) method Consumer.set(Derived,Collection<? extends Consumer>) is not applicabl e (argument mismatch; Base cannot be converted to Collection<? extends Consu mer>)

PD: Gracias a Holger, por señalar lo incompleto de mi respuesta.

Digamos que tenemos 2 clases. Una base vacía de clase y una subclase de esta clase Derived .

public class Base {} public class Derived extends Base {}

Luego tenemos algunos métodos en otra clase:

import java.util.Collection public class Consumer { public void test() { set(new Derived(), new Consumer().get()); } public <T extends Base> T get() { return (T) new Derived(); } public void set(Base i, Derived b) { System.out.println("base"); } public void set(Derived d, Collection<? extends Consumer> o) { System.out.println("object"); } }

Esto se compila y se ejecuta con éxito en Java 7, pero no se compila en Java 8. El error:

Error:(8, 9) java: reference to set is ambiguous both method set(Base,Derived) in Consumer and method set(Derived,java.util.Collection) in Consumer match

¿Por qué funciona en Java 7, pero no en Java 8? ¿Cómo podría <T extends Base> igualar Colección?


El problema es que se ha mejorado la inferencia de tipos. Tienes un método como

public <T extends Base> T get() { return (T) new Derived(); }

que básicamente dice: "la persona que llama puede decidir qué subclase de Base I devuelve", lo cual es una tontería obvia. Cada compilador debería darle una advertencia no verificada sobre su tipo de conversión (T) aquí.

Ahora tiene una llamada al método:

set(new Derived(), new Consumer().get());

Recuerde que su método Consumer.get() dice "la persona que llama puede decidir lo que devuelvo". Por lo tanto, es perfectamente correcto suponer que podría haber un tipo que extienda Base e implemente Collection al mismo tiempo. Entonces el compilador dice "No sé si llamar a set(Base i, Derived b) o set(Derived d, Collection<? extends Consumer> o) ".

Puede "arreglarlo" llamando a set(new Derived(), new Consumer().<Derived>get()); pero para ilustrar la locura de su método, tenga en cuenta que también puede cambiarlo a

public <X extends Base&Collection<Consumer>> void test() { set(new Derived(), new Consumer().<X>get()); }

que ahora llamará a set(Derived d, Collection<? extends Consumer> o) sin ninguna advertencia del compilador. La operación insegura real ocurrió dentro del método get .

Entonces, la solución correcta sería eliminar el parámetro de tipo del método get y declarar lo que realmente devuelve, Derived .

Por cierto, lo que me irrita es su afirmación de que este código podría compilarse en Java 7. Su inferencia de tipo limitada con llamadas a métodos anidados lleva a tratar el método get en un contexto de invocación anidado como devolver Base que no se puede pasar a Un método que espera un Derived . Como consecuencia, intentar compilar este código usando un compilador Java 7 compatible también fallará, pero por diferentes razones.