What is the type of an Optional created from a constant different to one created from a stream? [duplicate]

Let's say I have an interface Bar and an implementing class Foo:

interface Bar { ... }

class Foo implements Bar {
  static final Foo X = new Foo();
  static final Foo Y = new Foo();
  static final Foo[] FOOS = { X, Y };
}

Now let's say I have a class with methods that return Optional<Bar>:

public class Fubar {

  public Optional<Bar> fromValue() {
    return Optional.of(Foo.X); // This is fine
  }

  public Optional<Bar> fromStreamA() {
    
    Optional<Foo> foo = Arrays.asList(Foo.FOOS).stream().findFirst();
    
    return foo; // This fails to compile
  }

  public Optional<Bar> fromStreamB() {

    Optional<Foo> foo = Arrays.asList(Foo.FOOS).stream().findFirst();
    if (foo.isPresent()) {
      return Optional.of(foo.get()); // This is also fine
    }
    return Optional.empty();
  }
}

The method fromValue() compiles correctly, however fromStreamA() does not. I get an error message along the lines of Type mismatch: cannot convert from Optional<Foo> to Optional<Bar>. I worked around this mismatch in fromStreamB() (which compiles correctly).

I'm having trouble understanding why fromStreamA() does not compile. I sense it's something to do with generics and is similar to why List<Foo> cannot be assigned to List<Bar>, however I can't grasp it. Can anyone explain why fromValue() is fine and fromStreamA() is not.


It's because generics are not covariant: Optional<Foo> cannot be assigned to an Optional<Bar>.

The other example works because the compiler helps you infer the type to be Bar, i.e.:

Optional.of(Foo.X)

is equivalent to:

Optional.<Bar>of(Foo.X)

If you try to explicitly tell the inferred type to the compiler, it will not accept it:

Optional.<Foo>of(Foo.X);  // does not compile