当前位置: 首页 > 面试题库 >

在诸如Stream.reduce()之类的API中选择不变性的充分理由是什么?

王辉
2023-03-14
问题内容

回顾Java 8
StreamAPI设计,我对Stream.reduce()参数的通用不变性感到惊讶:

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

同一API的看似更通用的版本可能对的单个引用应用了协方差/协方差U,例如:

<U> U reduce(U identity,
             BiFunction<? super U, ? super T, ? extends U> accumulator,
             BiFunction<? super U, ? super U, ? extends U> combiner)

目前,这将允许以下操作(不可能):

// Assuming we want to reuse these tools all over the place:
BiFunction<Number, Number, Double> numberAdder =
    (t, u) -> t.doubleValue() + u.doubleValue();

// This currently doesn't work, but would work with the suggestion
Stream<Number> stream = Stream.of(1, 2L, 3.0);
double sum = stream.reduce(0.0, numberAdder, numberAdder);

解决方法,使用方法引用将类型“强制”为目标类型:

double sum = stream.reduce(0.0, numberAdder::apply, numberAdder::apply);

C#没有Func(T1, T2, TResult)使用声明位置方差定义如下的特定问题,这意味着使用Func此方法的任何API
都可以免费获得此行为:

public delegate TResult Func<in T1, in T2, out TResult>(
    T1 arg1,
    T2 arg2
)

与建议的设计相比,现有设计有哪些优势(可能还有进行EG决策的原因)?

或者,以不同的方式询问,我可能会忽略建议的设计的警告(例如,类型推断困难,并行化约束或约简操作的特定约束,例如,关联性,对Java未来声明位置的预期BiFunction<in T, in U, out R>, …)?


问题答案:

仔细浏览lambda开发的历史并找出做出该决定的“ THE”原因很困难-因此,最终,人们将不得不等待其中一名开发者回答此问题。

一些提示可能如下:

  • 流接口经历了多次迭代和重构。在最早的Stream接口版本之一中,有专用的reduce方法,而reduce在问题中最接近该方法的方法仍被调用Stream#fold。这个已经收到一个BinaryOperator作为combiner参数。

  • 有趣的是,很长一段时间以来,lambda建议包括一个专用接口Combiner<T,U,R>。违反直觉的是,此功能未在combiner中使用Stream#reduce。取而代之的是,它被用作reducer,这似乎是当今称为的accumulator。但是,该Combiner接口在以后的版本中被替换BiFunction

  • 与问题最相似的地方是在邮件列表中有关Stream#flatMap签名的线程中,然后将其转变为有关流方法签名差异的一般问题。他们将它们固定在某些地方,例如

正如Brian纠正我的那样:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

代替:

<R> Stream<R> flatMap(Function<T, Stream<? extends R>> mapper);

但是请注意,在某些地方这是不可能的:

T reduce(T identity, BinaryOperator<T> accumulator);

Optional<T> reduce(BinaryOperator<T> accumulator);

由于使用了“ BinaryOperator”,因此无法修复,但是如果使用“ BiFunction”,则我们具有更大的灵活性

<U> U reduce(U identity, BiFunction<? super U, ? super T, ? extends U> accumulator, BinaryOperator<U> combiner)

代替:

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

关于“ BinaryOperator”的相同评论

(我强调)。

我发现 替换为BinaryOperatora
的唯一理由BiFunction最终是在同一线程中对此语句的响应中给出的:

正如您所说,即使BinaryOperator引入了更大的灵活性,它也不会被BiFunction取代,BinaryOperator要求两个参数和返回类型相同,因此从概念上讲它的权重更大(EG已经对此进行了投票)。

也许有人可以从中得出决定这一决定的专家组投票的具体参考,但也许这句话已经充分回答了为什么是这样的问题。



 类似资料:
  • 回顾Java 8API设计时,我对参数的泛型不变性感到惊讶: 同一个API的一个看起来更通用的版本可能已经在对的单独引用上应用了协方差/逆方差,例如: 这将允许当前不可能的以下操作: 解决办法是使用方法引用将类型“强制”为目标类型: C#没有这个特殊的问题,因为定义如下,使用声明-站点方差,这意味着任何使用的API都可以免费获得这个行为: 与建议的设计相比,现有的设计有什么优势(以及可能的,决策的

  • 问题内容: 我还没有机会在学校参加任何严肃的低级编程课程。(我知道我真的应该继续学习“幕后知识”以成为更好的程序员。)我感谢Java的便利性,包括将任何内容粘贴到语句中的能力。但是,为什么有任何理由要使用它呢? 另外,我应该避免在“实际应用程序”中进行此类打印调用吗?使用某种UI功能将消息打印到客户端的显示屏上可能更好,对吧? 问题答案: PrintStream类的方法提供类似于C中函数的字符串格

  • 我正在尝试选择一些不在我的中的属性。这可能吗? 如何从中获取CatId和持久属性? 如果我将它们添加到组中,就像,那么我的结果就会混乱。 我可以使用获得其他属性,如,但这些属性不是计算值。 数据示例: 希望将结果按如下方式分组:

  • 问题内容: 我们站在哪里 我们正在努力实现网络可访问性,以便遵守某些规范公共/教育机构的法律。到目前为止,我们一直在确保: 我们的布局在逻辑上是有序的; 图片有标签。但很快就知道我们需要采取行动,并认真考虑一下。 我们在考虑什么 我们一直在寻找动态Web应用程序的框架,但担心它对我们的可访问性地位可能意味着什么。 我知道没有JavaScript的浏览器很可能会破坏高度动态的Angular应用程序(

  • 虽然你可以选择在浏览器端使用Less,直接在页面中嵌入一个 Less.js 文件,你也可以选择在服务器端使用Less,使用命令行将Less文件编译成最终的CSS文件。 然而,这两种方式都不够灵活,人们更喜欢使用图形界面工具进行编译。常见的工具有 winLess、simpLess、Koala 等,最值得推荐的编译工具,非 Koala 莫属。 Koala是由国人开放的一款开源的前端预处理器语言图形编译

  • 我没有从下面的 React 路由器获取文档: 但是现在Home不能参与路由,像onEnter hooks等。您在与帐户和报表相同的位置进行渲染,因此路由器允许您将Home作为带有IndexRoute的一级路由组件。 他这里路由是什么意思?什么是onEnter钩子?