至于“为什么”,相信在cmets中有不少争论。但是,我同意你的看法,因为没有 toList() 方法很烦人。 toIterable() 方法也是如此。
所以我将向您展示一个技巧,让您无论如何都可以使用这两种方法。幸运的是,Java 非常灵活,可以让您做各种有趣的事情。大约 10 年前,我读过 this article,它描述了一个将方法“插入”到任何给定接口的机智技巧。诀窍在于使用代理来调整没有您想要的方法的接口。多年来,我发现它具有适配器模式的所有优点,而缺乏所有缺点。这就是我所说的大事。
这是一个示例代码,只是为了展示这个想法:
public class Streams {
public interface EnhancedStream<T>
extends Stream<T> {
List<T> toList();
Iterable<T> toIterable();
}
@SuppressWarnings("unchecked")
public static <T> EnhancedStream<T> enhance(Stream<T> stream) {
return (EnhancedStream<T>) Proxy.newProxyInstance(
EnhancedStream.class.getClassLoader(),
new Class<?>[] {EnhancedStream.class},
(proxy, method, args) -> {
if ("toList".equals(method.getName())) {
return stream.collect(Collectors.toList());
} else if ("toIterable".equals(method.getName())) {
return (Iterable<T>) stream::iterator;
} else {
// invoke method on the actual stream
return method.invoke(stream, args);
}
});
}
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(-2, 1, 2, -5).
filter(n -> n > 0).map(n -> n * n);
List<Integer> list = Streams.enhance(stream1).toList();
System.out.println(list); // [1, 4]
Stream<Integer> stream2 = Stream.of(-2, 1, 2, -5).
filter(n -> n > 0).map(n -> n * n);
Iterable<Integer> iterable = Streams.enhance(stream2).toIterable();
iterable.forEach(System.out::println); // 1
// 4
}
}
这个想法是使用EnhancedStream 接口,通过定义您要添加的方法来扩展Java 的Stream 接口。然后,动态代理通过将原始 Stream 方法委托给正在适应的实际流来实现此扩展接口,同时它只是为新方法(Stream 中未定义的方法)提供内联实现。
此代理可通过透明地执行所有代理内容的静态方法获得。
请注意,我并不是说这是最终解决方案。相反,这只是一个可以高度改进的示例,即对于返回另一个 Stream 的 Stream 的每个方法,您也可以为该方法返回一个代理。这将允许链接EnhancedStreams(您需要在EnhancedStream 接口中重新定义这些方法,以便它们返回EnhancedStream 协变返回类型)。此外,缺少正确的异常处理,以及更健壮的代码来决定是否将方法的执行委托给原始流。