使用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循环。 在编程forif是两个基本的流量控制。 如果我们可以将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)目前还不清楚控制流方法(如ifExistifNotExist )之后的方法应该如何表现。 它们是应该总是被执行还是仅在条件下被调用(类似于ifExist )?

(3)名称ifExist意味着终端操作,因此没有任何东西可以返回 – void 。 一个很好的例子是来自Optional void ifPresent(Consumer)

解决方案

我会编写一个完全独立的类,它将独立于任何具体类和任何特定条件。

界面很简单,由两个无上下文控制流方法组成 – ifTrueifFalse

可以通过几种方法创建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(); }); 

除非你在一个方法中包含actionAactionC (听起来很糟糕):

 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); } } }