假设我有一个Person类
public class Person {
private final String name;
private final int age;
private boolean rejected;
private String rejectionComment;
public void reject(String comment) {
this.rejected = true;
this.rejectionComment = comment;
}
// constructor & getters are ommited
}
我的应用就是这样
class App {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("John", 10),
new Person("Sarah", 20),
new Person("Daniel", 30)
)
persons.forEach(p -> {
rejectIfYoungerThan15(p);
rejectIfNameStartsWithD(p);
// other rejection functions
}
}
private static void rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
p.reject("Too young")
}
}
private static void rejectIfNameStartsWithD(Person p) {
if (!p.isRejected() && p.getName().startsWith("D")) {
p.reject("Name starts with 'D'")
}
}
// other rejection functions
}
问题是我不喜欢必须在每个拒绝功能中执行!p.isRejected()
检查。而且,将已经被拒绝的人传递给下一个过滤器是没有意义的。
所以我的想法是使用Stream.filter
的机制,使之类似
persons.stream().filter(this::rejectIfYoungerThan15).filter(this::rejectIfNameStartsWithD)...
如果未通过传递的true
,则更改这些方法的签名以返回Person
,否则返回false
。
但是在我看来,将filter
与非纯函数一起使用是一个非常糟糕的主意。
您对如何更优雅地制作它有任何想法吗?
答案 0 :(得分:1)
当您将检查功能更改为仅检查条件(即不调用p.isRejected()
)并返回boolean
时,您已经采取了必要的短路步骤:
private static boolean rejectIfYoungerThan15(Person p) {
if(p.getAge() < 15) {
p.reject("Too young");
return true;
}
return false;
}
private static boolean rejectIfNameStartsWithD(Person p) {
if(p.getName().startsWith("D")) {
p.reject("Name starts with 'D'");
return true;
}
return false;
}
可用作
persons.forEach(p -> {
if(rejectIfYoungerThan15(p)) return;
if(rejectIfNameStartsWithD(p)) return;
// other rejection functions
}
}
Stream的filter
操作除了检查返回的boolean
值并纾困外没有任何其他作用。但是根据Stream的实际终端操作,短路可能会更远,最终导致无法检查所有元素,因此您不应在此处引入Stream操作。
答案 1 :(得分:0)
从lambda调用这些方法很好,但是,为了提高可读性,您可以重命名这些方法以显示其作用并返回boolean
,例如:
private boolean hasEligibleAge(Person p){..}
private boolean hasValidName(Person p){..}
另一种方法是将这些方法包装到另一种方法中(以反映业务逻辑/流程),例如:
private boolean isEligible(Person p){
//check age
//check name
}
答案 2 :(得分:0)
您应该使Person
不可变,并让拒绝方法返回一个新的Person
。这样您就可以链接map
个呼叫。像这样:
public class Person {
private final String name;
private final int age;
private final boolean rejected;
private final String rejectionComment;
public Person reject(String comment) {
return new Person(name, age, true, comment);
}
// ...
}
class App {
// ...
private static Person rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
return p.reject("Too young");
}
return p;
}
}
现在您可以执行以下操作:
persons.stream()
.map(App::rejectIfYoungerThan15)
.map(App::rejectIfNameStartsWithD)
.collect(Collectors.toList());
如果要删除被拒绝的人,可以在映射后添加过滤器:
.filter(person -> !person.isRejected())
编辑:
如果需要使拒绝短路,可以将拒绝功能组合为一个新功能,并使其在第一次拒绝后停止。像这样:
/* Remember that the stream is lazy, so it will only call new rejections
* while the person isn't rejected.
*/
public Function<Person, Person> shortCircuitReject(List<Function<Person, Person>> rejections) {
return person -> rejections.stream()
.map(rejection -> rejection.apply(person))
.filter(Person::isRejected)
.findFirst()
.orElse(person);
}
现在您的信息流可以如下所示:
List<Function<Person, Person>> rejections = Arrays.asList(
App::rejectIfYoungerThan15,
App::rejectIfNameStartsWithD);
List<Person> persons1 = persons.stream()
.map(shortCircuitReject(rejections))
.collect(Collectors.toList());