5

我有一些 Java 代码可以根据一些输入过滤列表。它目前使用 lambda,例如:

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects
                .stream()
                .filter( compObject -> allowedTags.contains(compObject.getTag()))
                .collect(Collectors.toList());
}

我想要做的是将过滤器逻辑移动到另一种方法,使其可重用且易于单元测试。所以我想使用方法引用来代替传递给过滤器方法的 lambda。如果过滤器逻辑是相当静态的(即允许的标签列表在编译时已知),则很容易做到,但我无法弄清楚如何使用过滤器中的动态数据来做到这一点。

我想要的是某种使用方法引用然后传递第二个动态参数的方法,即

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects
                .stream()
                .filter(this::filterByAllowedTags, allowedTags)
                .collect(Collectors.toList());
}

那么有没有可能做我想做的事,或者我可能错误地处理了这种情况?

4

3 回答 3

4

我建议传入 aPredicate作为参数。这样,调用者可以根据它想要的任何标准进行过滤,包括allowedTags或其他:

public List<ComplexObject> retrieveObjectsFilteredBy(Predicate<ComplexObject> pred) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects.stream()
        .filter(pred)
        .collect(Collectors.toList());
}

这将被称为:

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveObjectsFilteredBy(cobj -> allowedTags.contains(cobj.getTag()));

但是你可以走得更远,这取决于你愿意做多少重构。与其“检索”返回 a List,不如让它返回 a Stream?而不是返回过滤器方法返回 a List,让它也返回 a 怎么样Stream

public Stream<ComplexObject> retrieveObjectsFilteredBy2(Predicate<ComplexObject> pred) {
    Stream<ComplexObject> complexObjects = retrieveAllComplexObjects2();
    return complexObjects.filter(pred);
}

调用方看起来像这样:

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveObjectsFilteredBy2(cobj -> allowedTags.contains(cobj.getTag()))
            .collect(toList());

现在,如果您仔细查看,您会发现 retrieve-filter 方法根本没有添加任何值,因此您不妨将其内联到调用者中:

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(cobj -> allowedTags.contains(cobj.getTag()))
            .collect(toList());

当然,根据调用者想要做什么,它可能不想将结果收集到一个列表中;它可能想用forEach()或其他东西处理结果。

现在您仍然可以将过滤器分解为自己的方法,用于测试/调试,并且您可以使用方法参考:

boolean cobjFilter(ComplexObject cobj) {
    List<String> allowedTags = ... ;
    return allowedTags.contains(cobj.getTag());
}

    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(this::cobjFilter)
            .collect(toList());

如果您不希望过滤器内置允许的标签,则可以将其从谓词更改为返回谓词的高阶函数:

Predicate<ComplexObject> cobjFilter(List<String> allowedTags) {
    return cobj -> allowedTags.contains(cobj.getTag());
}

    List<String> allowedTags = ... ;
    List<ComplexObject> result =
        retrieveAllComplexObjects2()
            .filter(cobjFilter(allowedTags))
            .collect(toList());

这些变化中的哪一个最有意义取决于您的应用程序的外观以及您在过滤中需要什么样的动态性。

于 2014-10-29T18:38:54.607 回答
2

下面的呢?它以单独的方法提取谓词,使其易于测试,并且易于重用。

public Predicate<ComplexObject> tagAllowed(List<String> allowedTags) {
    return (ComplexObject co) -> allowedTags.contains(co.getTag());
}

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags) {
    List<ComplexObject> complexObjects = retrieveAllComplexObjects();
    return complexObjects
                .stream()
                .filter(tagAllowed(allowedTags))
                .collect(Collectors.toList());
}
于 2014-10-29T17:24:49.773 回答
2

方法参考的问题this::filterByAllowedTags在于它具有以下形状:

(ComplexObject, List<String>) -> boolean

但它被传递到filter()期望形状的 lambda 中:

(ComplexObject) -> boolean

换句话说,this::filterByAllowedTags永远不可能是 a Predicate,但它可能是一些替代的 interface Predicate2。然后你还需要一个重载的过滤器,它需要一个Predicate2.

Eclipse Collections(以前称为 GS Collections)的方法select()与.filter()selectWith()

使用select()

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
    // retrieveAllComplexObjects returns a MutableList, possibly FastList
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
    // select() returns MutableList here which extends List
    return complexObjects.select(compObject -> allowedTags.contains(compObject.getTag()));
}

使用selectWith()

public List<ComplexObject> retrieveObjectsFilteredByTags(List<String> allowedTags)
{
    // retrieveAllComplexObjects returns a MutableList, possibly FastList
    MutableList<ComplexObject> complexObjects = retrieveAllComplexObjects();
    // select() returns MutableList here which extends List
    return complexObjects.selectWith(this::filterByAllowedTags, allowedTags);
}

private boolean filterByAllowedTags(ComplexObject complexObject, List<String> allowedTags)
{
    return allowedTags.contains(complexObject.getTag());
}

注意:我是 Eclipse Collections 的提交者。

于 2014-10-30T21:42:31.897 回答