使用 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
Use Java lambda instead of 'if else'
提问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 ifExist
method 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 exist
condition 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 for
loop. In programming for
and if
are two basic flow controls. If we can use lambda for a for
loop, why is using lambda for if
bad idea?
在 Java 8 中,我们可以使用 lambda forEach 代替传统的for
循环。在编程中for
和if
是两个基本的流程控制。如果我们可以将 lambda 用于for
循环,为什么将 lambda 用于if
坏主意?
for (Element element : list) {
element.doSomething();
}
list.forEach(Element::doSomething);
In Java 8, there's Optional
with ifPresent, similar to my idea of ifExist:
在 Java 8 中,有Optional
ifPresent,类似于我对 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 if
clauses?
关于代码维护和可读性,如果我有以下代码,其中包含许多重复的简单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 if
clause 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 flow和domain 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 ifExist
implies 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 - ifTrue
and ifFalse
.
界面很简单,由两个无上下文的控制流方法组成 -ifTrue
和ifFalse
。
There can be a few ways to create a Condition
object. 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 Condition
into Element
:
此外,我们可以集成Condition
到Element
:
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 Condition
by Condition.of
:
获得一个Condition
通过Condition.of
:
Element element = new Element();
Condition.of(element, Element::exist)
.ifTrue(e -> { ... })
.ifFalse(e -> { ... })
.getOperand()
.otherElementMethod();
Obtaining a Condition
by 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 actionC
within e0.exist()
. How would the method reference Element::actionA
be changed?
想象一下你需要actionC
在e0.exist()
. 如何Element::actionA
更改方法引用?
It would be turned back into a lambda:
它会变成一个 lambda:
e0.ifExist(e -> { e.actionA(); e.actionC(); });
unless you wrap actionA
and actionC
in a single method (which sounds awful):
除非你用一个方法包装actionA
和actionC
(这听起来很糟糕):
e0.ifExist(Element::actionAAndC);
The lambda now is even less 'readable' then the if
was.
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 map
might 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 ......