Stream.findFirst 不同于 Optional.of?

2022-09-02 03:12:50

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

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

为什么编译而给出编译器错误getItems2getItems

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

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


答案 1

An 不是 的子类型。与其他编程语言不同,Java的泛型类型系统不知道“只读类型”或“输出类型参数”,因此它不明白只提供实例,并且可以在需要的地方工作。Optional<B>Optional<A>Optional<B>BOptional<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.ofget()BA

不幸的是,这种目标类型推断不能通过链接调用工作,因此

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

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

→ 解决方案是为调用提供显式类型: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


答案 2

您遇到的问题是泛型的继承。可选< B >不扩展可选< A >,因此无法按此方式返回。

我想象这样的事情:

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

艺术

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

会很好,这取决于你的需求。

编辑:转义一些字符


推荐