8

I encountered a weird problem using Optionals and anonymous classes:

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

The first two methods do not compile with the error

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

I'd expected that, since both implement the interface Bar all three approaches work. I also cannot figure out why the third option fixes the problem. Can anyone explain this please?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Mirco
  • 2,940
  • 5
  • 34
  • 57
  • 2
    The third one fixes the problem, because there the type of `Optional.of` is fixed to `Optional`. In all the other cases, it is `Optional`. I would have expected the other two to type-infer the appropriate common upper-bound of `Bar` as well, though. But apparently `Optional(bar).orElse(someOtherSubclassOfBar)` needs some hand-holding. – Thilo Dec 03 '19 at 13:57

3 Answers3

8

You can supplement the type on the first two using a type witness:

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

This allows the compiler to see that you are expecting a return of Optional<Bar>, which #orElse can then inferr to accepting any Bar

Rogue
  • 11,105
  • 5
  • 45
  • 71
5

You'd need to tell the Optional that you want a Bar for that interface.

Bar bar = new Bar();
Optional<Bar> o = Optional.of(new Bar() {}).orElse(bar);
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Nicktar
  • 5,548
  • 1
  • 28
  • 43
  • 1
    I know what generics are for. I just thought javac will infer the types as @Thilo said. – Mirco Dec 03 '19 at 14:02
  • 3
    Yeah, coming from Scala, having to spell this out is kind of lame. On the other hand, `javac` compiles so much faster, so ... – Thilo Dec 03 '19 at 14:03
  • @Mirco it infers 'anon type extending Bar'. If that's not what you want, you have to state your intent. – Nicktar Dec 03 '19 at 15:39
5

Case compiles1

Your Optional has the generic type Bar, because the variable bar has type Bar.

The anonymous class of type Foo$1 you create has Bar as a super type, thus the method compiles.

Case doesNotCompile

Here, Optional has the generic type Foo$1 and you are trying to pass an object of type Foo$2 into orElse which does not have Foo$1 as a super type. Hence the compile error.

Case doesNotCompile2

Similar to doesNotCompile, Optional has the generic type Foo$1 and you are trying to pass bar, a variable of type Bar into orElse which again does not have Foo$1 as a super type.


Avoiding these errors

Add a type witness to your call of Optional::of. This gives your Optional the generic type 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
  • 3,517
  • 2
  • 22
  • 47