I came to a point while refactoring using if {} else {}
code with Java optionals. While optimizing code it turned to a result to this:
Optional.of(myBoolean).filter(b -> b).ifPresent(b -> {/*my code if true*/});
I recognized filter(b -> b)
is not the way i wanted to use optionals thus i turned it to
If.is(myBoolean).then(b -> {/*my code if true*/}).orElse(b -> {/*my code if false*/});
The code for the If
class I wrote to handle this is:
public class If {
private boolean isTrue;
public If(final boolean value) {
isTrue = value;
}
public If then(final Consumer s) {
if (isTrue) {
s.accept(null);
}
return this;
}
public void orElse(final Consumer s) {
if (!isTrue) {
s.accept(null);
}
}
public static If is(final boolean value) {
return new If(value);
}
}
Is this a good (whatever good is or should be) approach using the if {} else {}
statement in Java?
2 Answers 2
My first instinct is that your code is a pretty "cool" way to rewrite if else statements, but I don’t think it gains you too much. If you have massive amounts of if trees and want to make them more readable, I would consider using polymorphism to your advantage or, when appropriate, strategy pattern.
If you want some help refactoring a certain if tree please feel free to post the code and we can help.
There are often, IMO, two big reasons to make a new abstraction.
1) to decrease the mental load on the maintainers of this code or
2) to increase the ability to add new features in the future.
Both of these are, of course, related. My question is, and feel free to comment below, does your if abstraction decrease maintainer mental load through simplicity or brevity and/or does your if abstraction improve the ability to add new features in the future? If not then you need to consider why you are doing it in the first place.
-
\$\begingroup\$ Well i guess the only advantage is to write code in one line readable from left to right. That was more or less the intention for this (escalated) codereview that lead to the question here. \$\endgroup\$Daniel Gschösser– Daniel Gschösser2019年07月05日 11:32:37 +00:00Commented Jul 5, 2019 at 11:32
-
2\$\begingroup\$ @DanielGschösser
If.is(myBoolean).then(b -> {/*my code if true*/}).orElse(b -> {/*my code if false*/});
can also be written asif (myBoolean) { /* my code if true */ } else { /* my code if false */ }
which can also be written in one line readable from left to right. Just because it can be written in more lines doesn't mean that it has to. \$\endgroup\$Simon Forsberg– Simon Forsberg2019年07月08日 09:42:39 +00:00Commented Jul 8, 2019 at 9:42
There is nothing inherently wrong with if-else statements.
Please do not use Optionals as replacement for if-else statements. They were not intended for that purpose. Optional was intended to convey the possibility of null-values in public interfaces in code, instead of just documentation, and only that. Even using optional in a private method is against it's intention (you're supposed to know what your own code does).
Please read this answer from one of the architects behind the Optional class: https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555
Exercise: Calculate the number of object creations and method calls involved in a Optional-if-else compared to a regular if-else and evaluate what effect the difference, if there is one, has on the optimizations.
Implementation
The If-class is intended to replace if-else-statements, but it does not follow the same logic as it allows multiple then-statements. It also lacks support for else-if constructs. Regular if-else statements do not consume anything so the parameter type of then
and orElse
should be Runnable
(the fact that you pass a null
to the consumer is a tell tale about code smell).
if-else
branches can be replaced by polymorphism. \$\endgroup\$if
/else
? \$\endgroup\$.filter(b -> b)
in the first code example, it would be the exact same thing without that part, right? \$\endgroup\$filter
methodOptional.of(true).ifPresent(b -> System.out.println("1: " + b));
Optional.of(false).ifPresent(b -> System.out.println("2: " + b));
Optional.of(true).filter(b -> b).ifPresent(x -> System.out.println("3: " + x));
Optional.of(false).filter(b -> b).ifPresent(x -> System.out.println("4: " + x));
The console output was1: true
2: false
3: true
and2: false
should not appear (as4: false
doesn't). Thereforefilter(b -> b)
is necessary. That led me to theIf
class. \$\endgroup\$false
booleans to an empty optional, I get it. \$\endgroup\$