Java 8 Optional cannot be applied to interface

Viewed 4763

Using Optional, I want to return a certain implementation (First or Second) of an interface according to the mapping result. This is the interface that First and Second implement:

public interface MyInterface {
    Number number();
}

The following Optional usage is erroneous:

final String string = ...                          // might be null
final Number number = Optional.ofNullable(string)
        .map(string -> new First())
        .orElse(new Second())                      // erroneous line
        .number();

orElse (com.mycompany.First) in Optional cannot be applied to (com.mycompany.Second)

Why is the line erroneous since both of the classes First and Second implement the interface MyInterface and the method MyInterface::number returns Number? How to implement this correctly?

5 Answers

I have discovered out that the method Optional::map returns U which doesn't allow apply returned First to another type such Second is. An explicit casting to its interface or requiring it within the map method is a way to go:

final Number number = Optional.ofNullable("")
        .<MyInterface>map(string -> new First())
        .orElse(new Second())
        .number(); 

__

Edit: I have found this out after posting the question. However, I am keeping both since I haven't found a similar solution anywhere else yet.

The problem is that Java infers the mapped type to be First, and Second is not an instance of First. You need to explicitly give Java a bit of a nudge to know the right type:

private static void main(String... args)
{
    final String string = "";
    final Number number = Optional.ofNullable(string)
        .<MyInterface>map(str -> new First())  // Explicit type specified 
        .orElse(new Second())
        .number();
}

This is a generic limitation of type inference along method chains. It's not limited to Optional.

There has been some suggestion to have type inference work along method chains. See this question: Generic type inference not working with method chaining?

Maybe in a future version of Java the compiler will be clever enough to figure this out. Who knows.

I would write that without an explicit cast:

Optional.ofNullable(string)
        .map(s -> {
             MyInterface m = new First();
             return m;  
        })
        .orElse(new Second())
        .number();

You can also write:

Optional.ofNullable(string)
    .map(s -> new First())
    .filter(MyInterface.class::isInstance)
    .map(MyInterface.class::map)
    .orElse(new Second())
    .number()

Or, add a utility function to your codebase:

// the Class Object is unused and only present so the Compiler knows which Type you actually want
public static <T, R> Function<? super T, R> mapAs(Function<? super T, ? extends R> mappingFunction, Class<R> clazz) {
    return mappingFunction::apply;
}
Optional.ofNullable(string)
    .map(mapAs(s -> new First(), MyInterface.class))
    .orElse(new Second())
    .number()

Well as the explanation stands true in other answers as well, its the type inferred while using the map that errors out the orElse in use, a cleaner way to represent the suggested solution would be :

Optional.ofNullable(string)
        .map(s -> (MyInterface) new First()) // casting rather than binding here
        .orElse(new Second())
        .number();
Related