首页 > 解决方案 > Stream.findFirst 与 Optional.of 不同?

问题描述

假设我有两个类和两个方法:

class Scratch {
    private class A{}
    private class B extends A{}

    public Optional<A> getItems(List<String> items){
        return items.stream()
             .map(s -> new B())
             .findFirst();
    }

    public Optional<A> getItems2(List<String> items){
        return Optional.of(
            items.stream()
                 .map(s -> new B())
                 .findFirst()
                 .get()
        );
    }
}

为什么getItems2编译时getItems会给出编译器错误

incompatible types: java.util.Optional<Scratch.B> cannot be converted to java.util.Optional<Scratch.A>

因此,当我返回get的值并用编译器再次包装它时,它会识别继承,但如果我直接使用.OptionalfindFirstOptional.offindFirst

标签: javajava-streamoptional

解决方案


AnOptional<B>不是 的子类型Optional<A>。与其他编程语言不同,Java 的泛型类型系统不知道“只读类型”或“输出类型参数”,因此它不理解Optional<B>只提供一个实例B并且可以在需要的地方工作Optional<A>

当我们写一个像

Optional<A> o = Optional.of(new B());

Java的类型推断使用目标类型来确定我们想要的

Optional<A> o = Optional.<A>of(new B());

这是有效的,new B()可以在需要实例的地方A使用。

这同样适用于

return Optional.of(
        items.stream()
             .map(s -> new B())
             .findFirst()
             .get()
    );

其中方法声明的返回类型用于推断Optional.of调用的类型参数并传递 的结果,其中需要get()的实例是有效的。BA

不幸的是,这种目标类型推断不适用于链式调用,因此对于

return items.stream()
     .map(s -> new B())
     .findFirst();

它不用于map通话。因此对于map调用,类型推断使用的类型,new B()其结果类型将是Stream<B>. 第二个问题是它findFirst()不是泛型的,在 a 上调​​用它Stream<T>总是会产生 a Optional<T>(并且 Java 的泛型不允许声明像 那样的类型变量<R super T>,因此在这里甚至不可能产生Optional<R>具有所需类型的 a )。

→ 解决方案是为map调用提供显式类型:

public Optional<A> getItems(List<String> items){
    return items.stream()
         .<A>map(s -> new B())
         .findFirst();
}

如前所述,仅出于完整性考虑,findFirst()它不是通用的,因此不能使用目标类型。链接允许类型更改的通用方法也可以解决问题:

public Optional<A> getItems(List<String> items){
    return items.stream()
         .map(s -> new B())
         .findFirst()
         .map(Function.identity());
}

但我建议使用为map调用提供显式类型的解决方案。


推荐阅读