谓词为lambda表达式但依赖于"外部"参数

时间:2016-05-13 20:14:20

标签: java lambda

我试图制作Predicate以便了解我的情况 完成了一些记录,但我需要一个可以随时间更新的外部记录列表;我还需要跟踪已经处理的那些。

这就是代码的样子:

// This one is at class level and gets updated in different places
Set<String> recipients ...

private void method(Iterable<CustomType> records) {
  final Set<String> doneWith = new HashSet<>();

  try {
    someService.doThis(records, record -> { // records is a list of CustomType
      boolean notify = Stream.of(doneWith, recipients)
        .noneMatch(s -> s.contains(record.getField()));

      if (notify) {
        doneWith.add(record.getField());
      }
      return notify;
    });
    recipients.addAll(doneWith);
  } catch (Exception e) {
    // TODO: Do something here
  }
}

...现在我想&#34;外化&#34;进入Predicate第二个参数someService.doThis(records, <this_one>。有线索吗?

这是一种电话:

public void doThis(Iterable<CustomType> records, Predicate<CustomType> notify) {
  records.forEach(r -> {
    // Do some stuff here
    if (notify.test(r)) {
      // Do some more stuff here
    }
  });
}

注意:主要原因是希望能够重复使用它的谓词,因为我可能需要它在其他地方,但截至目前,它也用于测试案例。

3 个答案:

答案 0 :(得分:0)

据我所知,你的功能真的只是:

record -> !recipients.contains(record.getField()) 
          && doneWith.add(record.getField())

答案 1 :(得分:0)

我认为您需要使用BiPredicate作为参数。

public void doThis(Iterable<CustomType> records, BiPredicate<CustomType, Iterable<CustomType> > shouldNotify) {
  final Set<String> doneWith = new HashSet<>();
  records.forEach(r -> {
    if (shouldNotify.test(r, doneWith)) {
      // Do something here
    }
  });
}

答案 2 :(得分:0)

如果你只想外化你的谓词,那么你只需要制作一个新的谓词:

新谓词:

@Builder
public class MyPredicate implements Predicate<CustomType> {
    private Set<String> doneWith;
    private Set<String> recipients;

    @Override
    public boolean test(CustomType record) {
        boolean notify = Stream.of(doneWith, recipients)
                .noneMatch(s -> s.contains(record.getField()));

        if (notify) {
            doneWith.add(record.getField());
        }
        return notify;
    }
}

新来电:

private void methodName(Iterable<CustomType> records) {
    someService.doThis(records, MyPredicate.builder().doneWith(doneWith).recipients(recipients).build());
    recipients.addAll(doneWith);
}

继续解决你的问题我虽然这可能会有所帮助。你的谓词应该基本上只做测试。您正在谓词中执行更改操作。如果您将其移出并进行一些更改,则可能需要稍微更改代码的结构:

新谓词:

@Builder
public class MyPredicate implements Predicate<CustomType> {
    private Set<String> listToFind;

    @Override
    public boolean test(CustomType record) {
        return listToFind.parallelStream()
                .noneMatch(s -> s.contains(record.getField()));
    }

}

您的新来电:

private void methodName(Iterable<CustomType> records) {
    someService.doThis(records,
            MyPredicate.builder().listToFind(recipients).build()
                    .and(MyPredicate.builder().listToFind(doneWith).build())
    );
    recipients.addAll(doneWith);
}

最后你的新服务:

// TODO: Please consider using injection for your doneWith Set
final Set<String> doneWith = new HashSet<>();

public void doThis(Iterable<CustomType> records, Predicate<CustomType> shouldNotify) {
    records.forEach(r -> {
        if (shouldNotify.test(r)) {
// TODO: You continue here...
            doneWith.add(r.getField());
        }
    });
}

因此,您现在有一个谓词,它实际上是两个谓词的 AND 条件。这样您就知道只有当所有集合都缺少customType时,您才能在 doneWith 集中添加元素。您的服务需要共享此集合,最好的方法是使用上下文依赖注入。如果你这样做,你不需要更改你的代码那么多,你将有一个通用的谓词,你可以通用和外部使用。如果你有至少两个输入变量,那么 BiPredicate 的想法是可行的。