Optional.orElse não compila com tipos anônimos

8

Encontrei um problema estranho usando Optionalclasses anônimas e s:

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() {
        });
    }
}

Os dois primeiros métodos não são compilados com o erro

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

Eu esperava isso, já que ambos implementam a interface, Bartodas as três abordagens funcionam. Também não consigo descobrir por que a terceira opção resolve o problema. Alguém pode explicar isso, por favor?

Mirco
fonte
2
O terceiro corrige o problema, porque o tipo de Optional.ofé fixo Optional<Bar>. Em todos os outros casos, é Optional<SubAnonymousSubclassOfBar>. Eu esperava que os outros dois inferissem também o limite superior comum apropriado Bar. Mas, aparentemente, Optional<SomeSubclassOfBar>(bar).orElse(someOtherSubclassOfBar)precisa de alguma ajuda.
Thilo

Respostas:

7

Você pode suplementar o tipo nos dois primeiros usando uma testemunha de tipo:

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

Isso permite que o compilador veja que você espera um retorno Optional<Bar>, do qual #orElse pode deduzir a aceitar qualquerBar

Vampiro
fonte
5

Você precisaria informar ao opcional que deseja uma barra para essa interface.

Bar bar = new Bar();
Optional<Bar> o = Optional.of(new Bar() {}).orElse(bar);
Nicktar
fonte
1
Eu sei para que servem os genéricos. Eu apenas pensei que javac inferiria os tipos como o @Thilo disse.
Mirco
3
Sim, vindo de Scala, ter que soletrar isso é meio idiota. Por outro lado, javaccompila muito mais rápido, então ...
Thilo
@Mirco deduz 'barra de extensão do tipo anon'. Se não é isso que você deseja, você deve declarar sua intenção.
Nicktar
5

Caso compiles1

Seu Optionaltem o tipo genérico Bar, porque a variável bartem o tipo Bar.

A classe anônima do tipo que Foo$1você cria possui Barum supertipo, portanto, o método é compilado.

Caso doesNotCompile

Aqui, Optionaltem o tipo genérico Foo$1e você está tentando passar um objeto do tipo Foo$2para o orElsequal não tem Foo$1um supertipo. Daí o erro de compilação.

Caso doesNotCompile2

Semelhante a doesNotCompile, Optionaltem o tipo genérico Foo$1e você está tentando passar bar, uma variável do tipo Barpara a orElsequal novamente não tem Foo$1um supertipo.


Evitando esses erros

Adicione uma testemunha de tipo à sua chamada de Optional::of. Isso fornece Optionalo 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
fonte