使用 Java lambda 而不是“if else”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49857232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 03:09:44  来源:igfitidea点击:

Use Java lambda instead of 'if else'

javaif-statementlambdajava-8

提问by yelliver

With Java 8, I have this code:

使用 Java 8,我有以下代码:

if(element.exist()){
    // Do something
}

I want to convert to lambda style,

我想转换为 lambda 风格,

element.ifExist(el -> {
    // Do something
});

with an ifExistmethod like this:

使用这样的ifExist方法:

public void ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
    }
}

But now I have else cases to call:

但现在我还有其他情况要打电话:

element.ifExist(el -> {
    // Do something
}).ifNotExist(el -> {
    // Do something
});

I can write a similar ifNotExist, and I want they are mutually exclusive (if the existcondition is true, there is no need to check ifNotExist, because sometimes, the exist() method takes so much workload to check), but I always have to check two times. How can I avoid that?

我可以写一个类似的ifNotExist,而且我希望它们是互斥的(如果exist条件为真,则不需要检查ifNotExist,因为有时,exist()方法检查的工作量很大),但我总是要检查两个次。我怎样才能避免这种情况?

Maybe the "exist" word make someone misunderstand my idea. You can imagine that I also need some methods:

也许“存在”这个词会让别人误解我的想法。你可以想象我还需要一些方法:

 ifVisible()
 ifEmpty()
 ifHasAttribute()

Many people said that this is bad idea, but:

很多人说这是个坏主意,但是:

In Java 8 we can use lambda forEach instead of a traditional forloop. In programming forand ifare two basic flow controls. If we can use lambda for a forloop, why is using lambda for ifbad idea?

在 Java 8 中,我们可以使用 lambda forEach 代替传统的for循环。在编程中forif是两个基本的流程控制。如果我们可以将 lambda 用于for循环,为什么将 lambda 用于if坏主意?

for (Element element : list) {
    element.doSomething();
}

list.forEach(Element::doSomething);

In Java 8, there's Optionalwith ifPresent, similar to my idea of ifExist:

在 Java 8 中,有OptionalifPresent,类似于我对 ifExist 的想法:

Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);

And about code maintenance and readability, what do you think if I have the following code with many repeating simple ifclauses?

关于代码维护和可读性,如果我有以下代码,其中包含许多重复的简单if子句,您怎么看?

    if (e0.exist()) {
        e0.actionA();
    } else {
        e0.actionB();
    }

    if (e1.exist()) {
        e0.actionC();
    }

    if (e2.exist()) {
        e2.actionD();
    }

    if (e3.exist()) {
        e3.actionB();
    }

Compare to:

相比于:

    e0.ifExist(Element::actionA).ifNotExist(Element::actionB);
    e1.ifExist(Element::actionC);
    e2.ifExist(Element::actionD);
    e3.ifExist(Element::actionB);

Which is better? And, oops, do you notice that in the traditional ifclause code, there's a mistake in:

哪个更好?而且,哎呀,你有没有注意到在传统的if子句代码中,有一个错误:

if (e1.exist()) {
    e0.actionC(); // Actually e1
}

I think if we use lambda, we can avoid this mistake!

我想如果我们使用 lambda,我们可以避免这个错误!

回答by Michael

It's called a 'fluent interface'. Simply change the return type and return this;to allow you to chain the methods:

它被称为“流畅的界面”。只需更改返回类型并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;
}

You could get a bit fancier and return an intermediate type:

你可以更高级一点并返回一个中间类型:

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

Sample usage:

示例用法:

element.ifExist(el -> {
    //do something
})
.otherwise(el -> {
    //do something else
});

回答by Eran

You can use a single method that takes two consumers:

您可以使用一个接受两个消费者的方法:

public void ifExistOrElse(Consumer<Element> ifExist, Consumer<Element> orElse) {
    if (exist()) {
        ifExist.accept(this);
    } else {
        orElse.accept(this);
    }
}

Then call it with:

然后调用它:

element.ifExistOrElse(
  el -> {
    // Do something
  },
  el -> {
    // Do something else
  });

回答by Andrew Tobilko

The problem

问题

(1) You seem to mix up different aspects - control flowand domain logic.

(1) 你似乎混淆了不同的方面 - control flowdomain logic

element.ifExist(() -> { ... }).otherElementMethod();
          ^                      ^
        control flow method     business logic method

(2) It is unclear how methods after a control flow method (like ifExist, ifNotExist) should behave. Should they be always executed or be called only under the condition (similar to ifExist)?

(2) 不清楚控制流方法(如ifExist, ifNotExist)之后的方法应该如何表现。它们应该始终执行还是仅在条件(类似于ifExist)下调用?

(3) The name ifExistimplies a terminal operation, so there is nothing to return - void. A good example is void ifPresent(Consumer)from Optional.

(3)ifExist顾名思义就是一个终端操作,所以没有什么可返回的 - void。一个很好的例子void ifPresent(Consumer)来自Optional.

The solution

解决方案

I would write a fully separated class that would be independent of any concrete class and any specific condition.

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

The interface is simple, and consists of two contextless control flow methods - ifTrueand ifFalse.

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

There can be a few ways to create a Conditionobject. I wrote a static factory method for your instance (e.g. element) and condition (e.g. Element::exist).

可以有几种方法来创建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;
    }

}

Moreover, we can integrate Conditioninto Element:

此外,我们可以集成ConditionElement

class Element {

    ...

    public Condition<Element> formCondition(Predicate<Element> condition) {
        return Condition.of(this, condition);
    }

}

The pattern I am promoting is:

我推广的模式是:

  • work with an Element;
  • obtain a Condition;
  • control the flow by the Condition;
  • switch back to the Element;
  • continue working with the Element.
  • 使用Element;
  • 获得一个Condition
  • 控制流量Condition;
  • 切换回Element;
  • 继续与Element.

The result

结果

Obtaining a Conditionby Condition.of:

获得一个Condition通过Condition.of

Element element = new Element();

Condition.of(element, Element::exist)
             .ifTrue(e -> { ... })
             .ifFalse(e -> { ... })
         .getOperand()
             .otherElementMethod();

Obtaining a Conditionby Element#formCondition:

获得一个Condition通过Element#formCondition

Element element = new Element();

element.formCondition(Element::exist)
           .ifTrue(e -> { ... })
           .ifFalse(e -> { ... })
       .getOperand()
           .otherElementMethod();

Update 1:

更新 1:

For other test methods, the idea remains the same.

对于其他测试方法,想法保持不变。

Element element = new Element();

element.formCondition(Element::isVisible);
element.formCondition(Element::isEmpty);
element.formCondition(e -> e.hasAttribute(ATTRIBUTE));

Update 2:

更新 2:

It is a good reason to rethink the code design. Neither of 2 snippets is great.

这是重新考虑代码设计的一个很好的理由。两个片段都不是很好。

Imagine you need actionCwithin e0.exist(). How would the method reference Element::actionAbe changed?

想象一下你需要actionCe0.exist(). 如何Element::actionA更改方法引用?

It would be turned back into a lambda:

它会变成一个 lambda:

e0.ifExist(e -> { e.actionA(); e.actionC(); });

unless you wrap actionAand actionCin a single method (which sounds awful):

除非你用一个方法包装actionAactionC(这听起来很糟糕):

e0.ifExist(Element::actionAAndC);

The lambda now is even less 'readable' then the ifwas.

lambda 现在比if过去更不“可读”了。

e0.ifExist(e -> {
    e0.actionA();
    e0.actionC();
});

But how much effort would we make to do that? And how much effort will we put into maintaining it all?

但是我们要为此付出多少努力?我们将投入多少精力来维护这一切?

if(e0.exist()) {
    e0.actionA();
    e0.actionC();
}

回答by Joop Eggen

As it almost but not really matches Optional, maybe you might reconsider the logic:

由于它几乎但不是真正匹配Optional,也许您可​​能会重新考虑逻辑:

Java 8 has a limited expressiveness:

Java 8 的表达能力有限:

Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));

Here the mapmight restrict the view on the element:

这里map可能会限制元素的视图:

element.map(el -> el.mySpecialView()).ifPresent(System.out::println);

Java 9:

爪哇 9:

element.ifPresentOrElse(el -> System.out.println("Present " + el,
                        () -> System.out.println("Not present"));

In general the two branches are asymmetric.

一般来说,这两个分支是不对称的。

回答by ag157

If you are performing a simple check on an object and then executing some statements based on the condition then one approach would be to have a Map with a Predicate as key and desired expression as value for example.

例如,如果您对对象执行简单检查,然后根据条件执行一些语句,那么一种方法是使用以 Predicate 作为键和所需表达式作为值的 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!");
}};

We could later stream the following Map to get the value when the Predicate returns true which could replace all the if/else code

我们稍后可以流式传输以下 Map 以在 Predicate 返回 true 时获取值,这可以替换所有 if/else 代码

        ruleMap.keySet()
               .stream()
               .filter((keyCondition)->keyCondition.test(numItems,version))
               .findFirst()
               .ifPresent((e)-> System.out.print(ruleMap.get(e).get()));

Since we are using findFirst() it is equivalent to if/else if /else if ......

由于我们使用的是 findFirst(),它等价于 if/else if /else if ......