tipos incompatibles cuando se utilizan los genéricos recursivas en Java

Niels Basjes:

He escrito el código de Java donde utilizo una forma recursiva de los genéricos para lograr una forma limpia de hacer un patrón hereditario del constructor.

Esto funciona, pero no entiendo algunas de las advertencias y los errores que recibo de que el compilador Java.

Esta es la versión simplificada severa de la parte que no entiendo:

package nl.basjes.test;

public class Foo<X extends Foo<X>> {
  public X doSomething() {
    return this;
  }
}

Para el "devolver este;" Me sale el error

Incompatible Types
Required: X
Found   : nl.basjes.test.Foo <X>

Ahora 'esto' es siempre una subclase de Foo (o incluso la propia Foo) y 'X' se define como X extends Foo<X>. Que yo sepa, estos deben ser "la misma", pero al parecer no lo son.

Así que en mi código añadí un yeso para la instrucción de retorno de esta manera:

package nl.basjes.test;

public class Foo<X extends Foo<X>> {
  public X doSomething() {
    return (X)this;
  }
}

que hace que la compilación de código y el funcionamiento esperado y deseado.

Yo sin embargo todavía una advertencia sobre "fundido sin control" por la misma razón que el anterior (pero ahora es sólo una advertencia).

$ javac -Xlint:unchecked nl/basjes/test/Foo.java 
nl/basjes/test/Foo.java:5: warning: [unchecked] unchecked cast
        return (X)this;
                  ^
  required: X
  found:    Foo<X>
  where X is a type-variable:
    X extends Foo<X> declared in class Foo
1 warning

¿Por qué no Java que ver X(que se extiende Foo<X>) y this(que se extiende Foo<X>) son compatibles?

En este punto mi mejor conjetura es que esto tiene que ver con una parte de la borradura tipo que no entiendo todavía.

ernest_k:

Si tenemos en cuenta los argumentos de tipo hormigón, se hace más fácil ver el problema:

Suponer

Foo<Bar> barFoo = ...;

Cuando se llama barFoo.doSomething(), que espera obtener un Barobjeto:

Bar bar = barFoo.doSomething()

Sin embargo, su aplicación real:

public X doSomething() {
  return this;
}

Más o menos puede ser llenado con los siguientes parámetros concretos:

public Bar doSomething() {
  return this; //But "this" is a Foo<Bar>, not a Bar.
}

Aquí está un ejemplo diferente para que sea aún más evidente:

class Bar extends Foo<Bar> {
}
class Baz extends Foo<Bar> { //note this is a Foo<Bar>
}

Y:

Baz baz = new Baz();
Bar bar = baz.doSomething();

En lo anterior, se espera baz.doSomething()para devolver una Bar, pero el código en doSomething()está volviendo una Baz, pero se convierte para que Bar, que tiene un problema de seguridad de tipos (de hecho, estos tipos son incompatibles, pero sólo obtendrá un ClassCastException cuando tiene diferentes clases como en el último ejemplo).

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=202611&siteId=1
Recomendado
Clasificación