Opcional. O Else no compila con tipos anónimos

8

Encontré un problema extraño usando Optionalsy clases anónimas:

public class Foo {
    interface Bar {
    }

    void doesNotCompile() {
        Optional.of(new Bar() {
        }).orElse(new Bar() {
        });
    }

    void doesNotCompile2() {
        final Bar bar = new Bar() {
        };
        Optional.of(new Bar() {
        }).orElse(bar);
    }

    void compiles1() {
        final Bar bar = new Bar() {
        };
        Optional.of(bar).orElse(new Bar() {
        });
    }
}

Los dos primeros métodos no se compilan con el error

java: incompatible types: <anonymous test.Foo.Bar> cannot be converted to <anonymous test.Foo.Bar>

Lo esperaba, ya que ambos implementan la interfaz, Barlos tres enfoques funcionan. Tampoco puedo entender por qué la tercera opción soluciona el problema. ¿Alguien puede explicar esto por favor?

Mirco
fuente
2
El tercero soluciona el problema, porque allí Optional.ofse fija el tipo de Optional<Bar>. En todos los demás casos, lo es Optional<SubAnonymousSubclassOfBar>. Sin embargo, también hubiera esperado que los otros dos dedujeran el límite superior común apropiado Bar. Pero aparentemente Optional<SomeSubclassOfBar>(bar).orElse(someOtherSubclassOfBar)necesita algo de mano.
Thilo

Respuestas:

7

Puede complementar el tipo en los dos primeros utilizando un testigo de tipo:

Optional.<Bar>of(new Bar(){}).orElse(new Bar(){});

Esto permite que el compilador vea que está esperando un retorno de lo Optional<Bar>que # o Else puede inferir para aceptar cualquierBar

Pícaro
fuente
5

Debería decirle a Opcional que desea una barra para esa interfaz.

Bar bar = new Bar();
Optional<Bar> o = Optional.of(new Bar() {}).orElse(bar);
Nicktar
fuente
1
Sé para qué sirven los genéricos. Solo pensé que javac inferiría los tipos como dijo @Thilo.
Mirco
3
Sí, viniendo de Scala, tener que explicar esto es un poco lamentable. Por otro lado, javaccompila mucho más rápido, así que ...
Thilo
@Mirco infiere 'anon type extendiendo Bar'. Si eso no es lo que quieres, debes declarar tu intención.
Nicktar
5

Caso compiles1

Tu Optionaltiene el tipo genérico Bar, porque la variable bartiene tipo Bar.

La clase de tipo anónimo Foo$1que crea tiene Barun supertipo, por lo tanto, el método se compila.

Caso doesNotCompile

Aquí, Optionaltiene el tipo genérico Foo$1y está intentando pasar un objeto de tipo Foo$2al orElseque no tiene Foo$1como supertipo. De ahí el error de compilación.

Caso doesNotCompile2

Similar a doesNotCompile, Optionaltiene el tipo genérico Foo$1y está intentando pasar bar, una variable de tipo Baren la orElseque nuevamente no tiene Foo$1como supertipo.


Evitando estos errores

Agregue un testigo tipo a su llamada de Optional::of. Esto le da Optionalel tipo genérico Bar:

public class Foo {

    interface Bar {
    }

    void doesNotCompile() {
        Optional.<Bar>of(new Bar() {
        }).orElse(new Bar() {
        });
    }

    void doesNotCompile2() {
        final Bar bar = new Bar() {
        };
        Optional.<Bar>of(new Bar() {
        }).orElse(bar);
    }

    void compiles1() {
        final Bar bar = new Bar() {
        };
        Optional.of(bar).orElse(new Bar() {
        });
    }
}
Marv
fuente