首页 > 解决方案 > 如何封装 Java Streams 操作

问题描述

我在使用 Java StreamAPI 时看到的缺点之一是,与迭代版本相比,代码的可读性似乎较差。

顺便说一句,这就是我的想法,但我只是提供一些背景信息(以避免XY 问题),我要问的问题与此无关,所以希望这不会被视为基于意见的问题。

封装通常会增加代码的可读性(同样,只是给定上下文),所以我正在寻找封装 JavaStream操作的最佳方法。

Stream例如,这是以典型方式(无封装)实现的一系列简单操作:

    void method1() {
        List<Integer> result = IntStream.range(1, 10)

                // Two Stream "intermediate" operations:
                .filter(i -> i % 2 != 0).map(i -> i * 2)

                .boxed().collect(Collectors.toList());

        log.debug("Result: {}", result);
        // Result: [2, 6, 10, 14, 18]
    }

这是我通过添加方法实现封装的第一个简单尝试filterOutEvenNumbersAndMultiplyByTwo

    IntStream filterOutEvenNumbersAndMultiplyByTwo(IntStream stream) {
        return stream.filter(i -> i % 2 != 0).map(i -> i * 2);
    }

    void method2() {
        List<Integer> result = filterOutEvenNumbersAndMultiplyByTwo(IntStream.range(1, 10))
                .boxed().collect(Collectors.toList());

        log.debug("Result: {}", result);
        // Result: [2, 6, 10, 14, 18]
    } 

如您所见,我Stream在一个新方法中封装了两个中间操作,这在这种情况下似乎没有多大意义,但这只是一个示例,可能有两个以上Stream的操作,也可能有更复杂的操作。

新方法增加了可读性(恕我直言)并且可以进行单元测试(这是事实)。
上面的代码有效,但它看起来不是很好或干净,理想情况下,我希望能够编写如下内容:

        List<Integer> result = IntStream.range(1, 10)

                // This obviously does not compile
                .filterOutEvenNumbersAndMultiplyByTwo()

                .boxed().collect(Collectors.toList());

我想它可以完成,但需要付出很大的努力,因为我必须实现Stream接口......

还有其他方法吗?
也许存在一个我可以使用的库?
也许我的 OO 思想正试图强制封装一些不应该的东西,但我仍然想知道这是否可能。

标签: javajava-stream

解决方案


filterOutEvenNumbers只是一个谓词。 multiplyByTwo是地图操作。

// we can unit test these
IntPredicate filterOutEvenNumbers = i -> i % 2 != 0
IntUnaryOperator multiplyByTwo = i -> i * 2;

您可以有 N 个谓词/映射操作。我们也可以在这里切换各种谓词/映射操作。

IntStream.range(1, 10)
    .filter(filterOutEvenNumbers)
    .map(multiplyByTwo)
    .boxed()
    .collect(Collectors.toList());

推荐阅读