使用Java lambda而不是’if else’
使用Java 8,我有这样的代码:
if(element.exist()){ // Do something }
我想转换为lambda风格,
element.ifExist(el -> { // Do something });
使用ifExist
方法,如下所示:
public void ifExist(Consumer consumer) { if (exist()) { consumer.accept(this); } }
但现在我还有其他情况要打电话:
element.ifExist(el -> { // Do something }).ifNotExist(el -> { // Do something });
我可以写一个类似的ifNotExist
,我希望它们是互斥的(如果exist
条件为真,则不需要检查ifNotExist
,因为有时, ifNotExist
()方法需要花费很多工作量来检查),但我总是有检查两次。 我怎么能避免这种情况?
也许“存在”这个词会让别人误解我的想法。 你可以想象我还需要一些方法:
ifVisible() ifEmpty() ifHasAttribute()
很多人说这是个坏主意,但是:
在Java 8中,我们可以使用lambda forEach而不是传统的for
循环。 在编程for
, if
是两个基本的流量控制。 如果我们可以将lambda用于for
循环,那么为什么使用lambda呢?
for (Element element : list) { element.doSomething(); } list.forEach(Element::doSomething);
在Java 8中,ifPresent是Optional
,类似于我对ifExist的想法:
Optional 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,我们可以避免这个错误!
因为它几乎但不是真正匹配Optional ,也许您可能会重新考虑逻辑:
Java 8的表达能力有限:
Optional 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"));
通常,两个分支是不对称的。
它被称为“流畅的界面” 。 只需更改返回类型并return this;
允许你链接方法:
public MyClass ifExist(Consumer consumer) { if (exist()) { consumer.accept(this); } return this; } public MyClass ifNotExist(Consumer consumer) { if (!exist()) { consumer.accept(this); } return this; }
你可以得到一点发烧友并返回一个中间类型:
interface Else { public void otherwise(Consumer consumer); // 'else' is a keyword } class DefaultElse implements Else { private final T item; DefaultElse(final T item) { this.item = item; } public void otherwise(Consumer consumer) { consumer.accept(item); } } class NoopElse implements Else { public void otherwise(Consumer consumer) { } } public Else ifExist(Consumer consumer) { if (exist()) { consumer.accept(this); return new NoopElse<>(); } return new DefaultElse<>(this); }
样品用法:
element.ifExist(el -> { //do something }) .otherwise(el -> { //do something else });
您可以使用一个需要两个使用者的方法:
public void ifExistOrElse(Consumer ifExist, Consumer orElse) { if (exist()) { ifExist.accept(this); } else { orElse.accept(this); } }
然后用:
element.ifExistOrElse( el -> { // Do something }, el -> { // Do something else });
问题
(1)你似乎混淆了不同的方面 – 控制流和域逻辑 。
element.ifExist(() -> { ... }).otherElementMethod(); ^ ^ control flow method business logic method
(2)目前还不清楚控制流方法(如ifExist
, ifNotExist
)之后的方法应该如何表现。 它们是应该总是被执行还是仅在条件下被调用(类似于ifExist
)?
(3)名称ifExist
意味着终端操作,因此没有任何东西可以返回 – void
。 一个很好的例子是来自Optional
void ifPresent(Consumer)
。
解决方案
我会编写一个完全独立的类,它将独立于任何具体类和任何特定条件。
界面很简单,由两个无上下文控制流方法组成 – ifTrue
和ifFalse
。
可以通过几种方法创建Condition
对象。 我为您的实例(例如element
)和条件(例如Element::exist
)编写了一个静态工厂方法。
public class Condition { private final Predicate condition; private final E operand; private Boolean result; private Condition(E operand, Predicate condition) { this.condition = condition; this.operand = operand; } public static Condition of(E element, Predicate condition) { return new Condition<>(element, condition); } public Condition ifTrue(Consumer consumer) { if (result == null) result = condition.test(operand); if (result) consumer.accept(operand); return this; } public Condition ifFalse(Consumer 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 formCondition(Predicate condition) { return Condition.of(this, condition); } }
我推广的模式是:
- 使用
Element
; - 获得
Condition
; - 按
Condition
控制流量; - 切换回
Element
; - 继续使用
Element
。
结果
通过Condition.of
获得Condition.of
:
Element element = new Element(); Condition.of(element, Element::exist) .ifTrue(e -> { ... }) .ifFalse(e -> { ... }) .getOperand() .otherElementMethod();
按Element#formCondition
获取Condition
Element#formCondition
:
Element element = new Element(); element.formCondition(Element::exist) .ifTrue(e -> { ... }) .ifFalse(e -> { ... }) .getOperand() .otherElementMethod();
更新1:
对于其他测试方法,这个想法保持不变。
Element element = new Element(); element.formCondition(Element::isVisible); element.formCondition(Element::isEmpty); element.formCondition(e -> e.hasAttribute(ATTRIBUTE));
更新2:
重新考虑代码设计是一个很好的理由。 两个片段都不是很好。
想象一下,你需要在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(); }
作为一些建议,我有一个缓存布尔值的想法:
public class Element { private Boolean exist; public void ifExist(Consumer consumer) { if (exist == null) { exist = exist(); } if (exist) { consumer.accept(this); } } public void ifNotExist(Consumer consumer) { if (exist == null) { exist = exist(); } if (!exist) { consumer.accept(this); } } }