与 flatMap 相比,为什么 mapMulti 需要类型信息

nim*_*o23 7 java java-stream java-16 java-17

我想使用mapMulti而不是flatMap重构以下代码:

// using flatMap (version 1) => returns Set<Item>
var items = users.stream()
                 .flatMap(u -> u.getItems().stream())
                .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

进入这个(版本2):

// using mapMulti (version 2) => returns Set<Item>
var items = users.stream()
                 .<Item>mapMulti((u, consumer) -> u.getItems().forEach(consumer))
                 .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

两者都返回相同的元素。然而,我怀疑我是否真的应该用flatMap更冗长的mapMulti. 为什么需要在mapMuli( .<Item>mapMulti)之前添加类型信息。如果我不包含类型信息,它将返回一个Set<Object>. (如何)我可以简化吗mapMulti

Swe*_*per 10

请注意,在使用时推断结果流类型所需的类型推断flatMap与使用时有很大不同mapMulti

当您使用 时flatMap,结果流的类型与 lambda 主体的返回类型相同。这是一个特殊的事情,编译器被设计为从中推断类型变量(即编译器“知道”它)。

但是,在 的情况下mapMulti,您可能想要的结果流的类型只能从您对consumerlambda 参数执行的操作推断出来。假设,编译器可以设计为,例如,如果您说过consumer.accept(1),那么它会查看您传递给 的内容accept,并查看您想要一个Stream<Integer>,并且在 的情况下,是该类型可以具有getItems().forEach(consumer)的唯一位置Itemcome from 是 的返回类型getItems,因此需要查看它。

您基本上是要求编译器根据 lambda 内部任意表达式的类型来推断 lambda 的参数类型。编译器根本就没有被设计来执行此操作。

除了添加前缀之外<Item>,还有其他(更长)的方法让它推断 aStream<Item>作为 的返回类型mapMulti

使 lambda 显式键入:

var items = users.stream()
             .mapMulti((User u, Consumer<Item> consumer) -> u.getItems().forEach(consumer))
             .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

添加临时流变量:

// By looking at the type of itemStream, the compiler can figure out that mapMulti should return a Stream<Item>
Stream<Item> itemStream = users.stream()
             .mapMulti((u, consumer) -> u.getItems().forEach(consumer));
var items = itemStream.collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

我不知道这是否更“简化”,但我认为如果使用方法引用会更整洁:

var items = users.stream()
             .map(User::getItems)
             .<Item>mapMulti(Iterable::forEach)
             .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

  • @Sweeper你一直说“编译器”,好像编译器实现在这里有任何纬度。你真正谈论的是_语言_,类型推断之类的东西如何工作在_语言规范_(JLS Ch 18)中清楚地说明了。编译器没有自由度来改变它接受的语言;正如规范中所规定的,这只是语言定义的范围。 (2认同)
  • @BrianGoetz 我完全同意。正是语言规范导致编译器如此。我的措辞有点宽松。 (2认同)