With Java 8, I have this code:
if(element.exist()){
// Do something
}
I want to convert to lambda style,
element.ifExist(el -> {
// Do something
});
with an ifExist
method like this:
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?
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?
for (Element element : list) {
element.doSomething();
}
list.forEach(Element::doSomething);
In Java 8, there's Optional
with ifPresent, similar to my idea of 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 (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 (e1.exist()) {
e0.actionC(); // Actually e1
}
I think if we use lambda, we can avoid this mistake!
The problem
(1) You seem to mix up different aspects - control flow and domain logic.
(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 toifExist
)?(3) The name
ifExist
implies a terminal operation, so there is nothing to return -void
. A good example isvoid ifPresent(Consumer)
fromOptional
.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
andifFalse
.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
).Moreover, we can integrate
Condition
intoElement
:The pattern I am promoting is:
Element
;Condition
;Condition
;Element
;Element
.The result
Obtaining a
Condition
byCondition.of
:Obtaining a
Condition
byElement#formCondition
:Update 1:
For other test methods, the idea remains the same.
Update 2:
It is a good reason to rethink the code design. Neither of 2 snippets is great.
Imagine you need
actionC
withine0.exist()
. How would the method referenceElement::actionA
be changed?It would be turned back into a lambda:
unless you wrap
actionA
andactionC
in a single method (which sounds awful):The lambda now is even less 'readable' then the
if
was.But how much effort would we make to do that? And how much effort will we put into maintaining it all?
As it almost but not really matches Optional, maybe you might reconsider the logic:
Java 8 has a limited expressiveness:
Here the
map
might restrict the view on the element:Java 9:
In general the two branches are asymmetric.
It's called a 'fluent interface'. Simply change the return type and
return this;
to allow you to chain the methods:You could get a bit fancier and return an intermediate type:
Sample usage:
You can use a single method that takes two consumers:
Then call it with:
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.
We could later stream the following Map to get the value when the Predicate returns true which could replace all the if/else code
Since we are using findFirst() it is equivalent to if/else if /else if ......