使用Java 8,我有这段代码:
if(element.exist()){
// Do something
}
我想转换为lambda风格,
element.ifExist(el -> {
// Do something
});
使用ifExist
方法,如下所示:
public void ifExist(Consumer<Element> consumer) {
if (exist()) {
consumer.accept(this);
}
}
但现在我还有其他情况要打电话:
element.ifExist(el -> {
// Do something
}).ifNotExist(el -> {
// Do something
});
我可以写一个类似的ifNotExist
,我希望它们是互斥的(如果exist
条件为真,则不需要检查ifNotExist
,因为有时,存在()方法需要花费很多工作量来检查),但我总是要检查两次。我怎么能避免这种情况?
也许“存在”这个词会让别人误解我的想法。你可以想象我还需要一些方法:
ifVisible()
ifEmpty()
ifHasAttribute()
很多人说这是个坏主意,但是:
在Java 8中,我们可以使用lambda forEach而不是传统的for
循环。在编程中for
和if
是两个基本的流控制。如果我们可以将lambda用于for
循环,为什么使用lambda来表示if
坏主意呢?
for (Element element : list) {
element.doSomething();
}
list.forEach(Element::doSomething);
在Java 8中,Optional
有ifPresent,类似于我对ifExist的看法:
Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
关于代码维护和可读性,如果我有以下代码并且有许多重复的简单if
子句,您认为怎么样?
if (e0.exist()) {
e0.actionA();
} else {
e0.actionB();
}
if (e1.exist()) {
e0.actionC();
}
if (e2.exist()) {
e2.actionD();
}
if (e3.exist()) {
e3.actionB();
}
比较:
e0.ifExist(Element::actionA).ifNotExist(Element::actionB);
e1.ifExist(Element::actionC);
e2.ifExist(Element::actionD);
e3.ifExist(Element::actionB);
哪个更好?而且,oops,您是否注意到在传统的if
子句代码中,出现了错误:
if (e1.exist()) {
e0.actionC(); // Actually e1
}
我认为如果我们使用lambda,我们可以避免这个错误!
答案 0 :(得分:31)
由于它几乎与可选不匹配,也许您可能会重新考虑逻辑:
Java 8表达能力有限:
Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));
此处map
可能会限制元素上的视图:
element.map(el -> el.mySpecialView()).ifPresent(System.out::println);
Java 9:
element.ifPresentOrElse(el -> System.out.println("Present " + el,
() -> System.out.println("Not present"));
一般来说,这两个分支是不对称的。
答案 1 :(得分:19)
它被称为'fluent interface'。只需更改返回类型和return this;
即可链接方法:
public MyClass ifExist(Consumer<Element> consumer) {
if (exist()) {
consumer.accept(this);
}
return this;
}
public MyClass ifNotExist(Consumer<Element> consumer) {
if (!exist()) {
consumer.accept(this);
}
return this;
}
你可能会有点发烧友并返回一个中间类型:
interface Else<T>
{
public void otherwise(Consumer<T> consumer); // 'else' is a keyword
}
class DefaultElse<T> implements Else<T>
{
private final T item;
DefaultElse(final T item) { this.item = item; }
public void otherwise(Consumer<T> consumer)
{
consumer.accept(item);
}
}
class NoopElse<T> implements Else<T>
{
public void otherwise(Consumer<T> consumer) { }
}
public Else<MyClass> ifExist(Consumer<Element> consumer) {
if (exist()) {
consumer.accept(this);
return new NoopElse<>();
}
return new DefaultElse<>(this);
}
样本用法:
element.ifExist(el -> {
//do something
})
.otherwise(el -> {
//do something else
});
答案 2 :(得分:11)
您可以使用一种需要两个消费者的方法:
public void ifExistOrElse(Consumer<Element> ifExist, Consumer<Element> orElse) {
if (exist()) {
ifExist.accept(this);
} else {
orElse.accept(this);
}
}
然后用:
调用它element.ifExistOrElse(
el -> {
// Do something
},
el -> {
// Do something else
});
答案 3 :(得分:3)
(1)您似乎混淆了不同方面 - control flow和domain logic。
element.ifExist(() -> { ... }).otherElementMethod();
^ ^
control flow method business logic method
(2)目前尚不清楚控制流方法(如ifExist
,ifNotExist
)之后的方法应如何表现。它们应该始终执行还是仅在条件下调用(类似于ifExist
)?
(3)名称ifExist
意味着终端操作,因此无法返回 - void
。一个很好的例子是来自void ifPresent(Consumer)
的Optional
。
我会编写一个完全独立的类,它独立于任何具体类和任何特定条件。
界面很简单,由两个无环境控制流方法组成 - ifTrue
和ifFalse
。
可以通过几种方法创建Condition
对象。我为您的实例编写了一个静态工厂方法(例如element
)和条件(例如Element::exist
)。
public class Condition<E> {
private final Predicate<E> condition;
private final E operand;
private Boolean result;
private Condition(E operand, Predicate<E> condition) {
this.condition = condition;
this.operand = operand;
}
public static <E> Condition<E> of(E element, Predicate<E> condition) {
return new Condition<>(element, condition);
}
public Condition<E> ifTrue(Consumer<E> consumer) {
if (result == null)
result = condition.test(operand);
if (result)
consumer.accept(operand);
return this;
}
public Condition<E> ifFalse(Consumer<E> consumer) {
if (result == null)
result = condition.test(operand);
if (!result)
consumer.accept(operand);
return this;
}
public E getOperand() {
return operand;
}
}
此外,我们可以将Condition
整合到Element
:
class Element {
...
public Condition<Element> formCondition(Predicate<Element> condition) {
return Condition.of(this, condition);
}
}
我推广的模式是:
Element
; Condition
; Condition
; Element
; Element
。 Condition
获取Condition.of
:
Element element = new Element();
Condition.of(element, Element::exist)
.ifTrue(e -> { ... })
.ifFalse(e -> { ... })
.getOperand()
.otherElementMethod();
Condition
获取Element#formCondition
:
Element element = new Element();
element.formCondition(Element::exist)
.ifTrue(e -> { ... })
.ifFalse(e -> { ... })
.getOperand()
.otherElementMethod();
对于其他测试方法,这个想法保持不变。
Element element = new Element();
element.formCondition(Element::isVisible);
element.formCondition(Element::isEmpty);
element.formCondition(e -> e.hasAttribute(ATTRIBUTE));
重新考虑代码设计是一个很好的理由。两个片段都不是很好。
想象一下,actionC
中需要e0.exist()
。如何更改方法引用Element::actionA
?
它将变回lambda:
e0.ifExist(e -> { e.actionA(); e.actionC(); });
除非你将actionA
和actionC
包装在一个方法中(听起来很糟糕):
e0.ifExist(Element::actionAAndC);
现在,lambda的可读性更低了。那么if
就是。
e0.ifExist(e -> {
e0.actionA();
e0.actionC();
});
但我们要做多少努力呢?我们将多少努力来维持这一切?
if(e0.exist()) {
e0.actionA();
e0.actionC();
}
答案 4 :(得分:1)
如果要在对象上执行简单检查,然后根据条件执行某些语句,则一种方法将是使用以谓词为键和所需表达式为值的Map 例如。
Map<Predicate<Integer>,Supplier<String>> ruleMap = new HashMap<Predicate<Integer>,Supplier<String>>(){{
put((i)-> i<10,()->"Less than 10!");
put((i)-> i<100,()->"Less than 100!");
put((i)-> i<1000,()->"Less than 1000!");
}};
稍后我们可以流式传输以下Map以获得谓词返回true时的值,该值可以替换所有if / else代码
ruleMap.keySet()
.stream()
.filter((keyCondition)->keyCondition.test(numItems,version))
.findFirst()
.ifPresent((e)-> System.out.print(ruleMap.get(e).get()));
因为我们使用的是findFirst(),所以它等效于if / else if / else if ......