Stream.findFirst与Optional.of不同?

kea*_*nni 21 java optional java-stream

可以说我有两个类和两个方法:

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()
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

incompatible types: java.util.Optional<Scratch.B> cannot be converted to java.util.Optional<Scratch.A>
Run Code Online (Sandbox Code Playgroud)

所以,当我返回get的值和使用编译器再次包装它时,会识别继承,但如果我直接使用结果,则不会.OptionalfindFirstOptional.offindFirst

Hol*_*ger 21

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

当我们写一个类似的语句时

Optional<A> o = Optional.of(new B());
Run Code Online (Sandbox Code Playgroud)

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

Optional<A> o = Optional.<A>of(new B());
Run Code Online (Sandbox Code Playgroud)

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

这同样适用于

return Optional.of(
        items.stream()
             .map(s -> new B())
             .findFirst()
             .get()
    );
Run Code Online (Sandbox Code Playgroud)

其中方法的声明返回类型用于推断Optional.of调用的类型参数并传递结果,其中get()一个实例的B位置A是有效的.

不幸的是,这种目标类型推断不能通过链式调用工作,所以对于

return items.stream()
     .map(s -> new B())
     .findFirst();
Run Code Online (Sandbox Code Playgroud)

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

→解决方案是为map呼叫提供显式类型:

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

只是为了完整性,如上所述,findFirst()不是通用的,因此,不能使用目标类型.链接允许类型更改的通用方法也可以解决问题:

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

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


Wor*_*ess 8

你遇到的问题是泛型的继承.可选<B>不扩展Optional <A>,因此无法返回.

我想象这样的事情:

public Optional<? extends A> getItems( List<String> items){
    return items.stream()
        .map(s -> new B())
        .findFirst();
}
Run Code Online (Sandbox Code Playgroud)

要么:

public Optional<?> getItems( List<String> items){
    return items.stream()
        .map(s -> new B())
        .findFirst();
}
Run Code Online (Sandbox Code Playgroud)

根据您的需要,可以正常工作.

编辑:转义一些字符

  • 有一个[泛型使用指南](https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html),它说:"*应该避免使用通配符作为返回类型,因为它强制程序员使用代码来处理通配符*"从我的实践经验来看,这是一个很好的建议.毕竟,返回"Optional <A>"可以轻松实现,请参阅[此答案](/sf/answers/3836143461/). (3认同)

Era*_*ran 6

一个Optional<B>不是一个子类的Optional<A>

在第一种情况下,您有个Stream<B>,因此findFirst返回Optional<B>,但不能转换为Optional<A>

在第二种情况下,您有一个流管道返回一个实例B。当您将该实例传递给时Optional.of(),编译器会看到该方法的返回类型为Optional<A>,因此Optional.of()返回一个Optional<A>(因为an Optional<A>可以将其实例保存B为其值(因为Bextends A))。