这是关于API设计的问题。在C#中添加扩展方法时,IEnumerable
获得了所有在所有集合上直接使用lambda表达式的方法。
随着Java中lambdas和默认方法的出现,我希望Collection
能够实现Stream
并为其所有方法提供默认实现。这样,我们就不需要调用stream()
来利用它提供的功能。
图书馆建筑师选择不太方便的方法的原因是什么?
答案 0 :(得分:8)
来自Maurice Naftalin's Lambda FAQ:
Why are Stream operations not defined directly on Collection?
API的早期草稿在[{1}}或
filter
上公开了map
,reduce
和Collection
等方法。但是,用户使用此设计的体验导致将“流”方法更正式地分离为他们自己的抽象。理由包括:
Iterable
等Collection
上的方法进行就地修改,与本质上更具功能性的新方法形成对比。在同一抽象上混合两种不同的方法迫使用户跟踪哪些是哪种。例如,给出声明removeAll
两个非常相似的方法调用
Collection strings;
会有惊人的不同结果;第一个将从集合中删除所有空
strings.removeAll(s -> s.length() == 0); strings.filter(s -> s.length() == 0); // not supported in the current API
个对象,而第二个将返回包含所有非空String
的流,而对集合没有影响。相反,当前的设计确保只能过滤明确获得的流:
String
其中省略号表示进一步的流操作,以终止操作结束。这使读者对过滤器的作用有了更清晰的直觉;
随着惰性方法被添加到
strings.stream().filter(s.length() == 0)...;
,用户感到困惑的是一种感知但错误的需要来推断该集合是处于“懒惰模式”还是“急切模式”。不是为Collection
增加新功能和不同功能,而是提供具有新功能的Collection
视图更清晰;添加到
Stream
的方法越多,与现有第三方实施名称冲突的可能性就越大。只添加一些方法(Collection
,stream
),冲突的可能性就会大大降低;访问并行视图仍需要视图转换;顺序流和并行流视图之间的不对称是不自然的。比较,例如
parallel
与
coll.filter(...).map(...).reduce(...);
这种不对称性在API文档中尤为明显,其中
coll.parallel().filter(...).map(...).reduce(...);
将有许多新方法来生成顺序流,但只有一个生成并行流,然后它们将具有与{{1}相同的方法}}。将这些分解为一个单独的界面,Collection
说,没有用;反直觉地,这仍然需要由Collection
和StreamOps
实施;对意见进行统一处理也为将来的其他观点留出了空间。
答案 1 :(得分:1)
集合 代表一组对象,称为其元素。
支持顺序和并行汇总 操作 > p>
通过这种方式,流是特定集合。不是这样的。因此,无论向后兼容性如何,Collection都不应实现Stream。
那么为什么Stream<T>
实施Collection<T>
呢?因为这是另一种看待一堆物体的方式。不是作为一组元素,而是通过您可以对其执行的操作。因此,这就是为什么我说Collection是一个对象模型而Stream是一个主题模型
答案 2 :(得分:0)
首先,来自Stream
的文档:
集合和流虽然有一些肤浅的相似之处,却有不同的目标。馆藏主要关注其元素的有效管理和访问。相比之下,流不提供直接访问或操纵其元素的方法,而是关注声明性地描述它们的源以及将在该源上聚合执行的计算操作。
所以你想保持流和集合appart的概念。如果Collection
将实现Stream
,则每个集合都是一个流,从概念上讲它不是。现在的方式,每个集合都可以为你提供一个适合该集合的流,如果你考虑它就会有所不同。
另一个想到的因素是内聚/耦合以及封装。如果每个实现Collection
的类都必须实现Stream
的操作,那么它将有两种(某种)不同的目的,并且可能会变得太长。
答案 3 :(得分:-1)
我的猜测是,这样做是为了避免使用现有代码实现Collection的破坏。很难提供一个适用于所有现有实现的默认实现。