Hello there, writing again after long time. Going
by the rate of increase in intervals between my blog posts, I won’t be
surprised if my blog ends up having higher frequency than Halley’s Comet, and may be scientists will develop some complex space-time equations to find out when the next blog will be published (in scale of light years). Constant used in these equations will be called Folderol's constant. Anyway,
let’s keep these orbital calculations aside for now, we will surely discuss it sometime
in future (before Hyakutake
Comet comes back).
Voila! We have just changed the place of the if statement. But the code now looks clean and if-free, doesn’t it? Below is an example of how to use it in case of an array.
As arrays need type infotmation, the methods takes class type as an argument.
2. Multiple checks of instanceof operator
Consider a scenario where you have a reference of super class and you are getting an object of one of its sub classes a runtime. Now, you want to perform specific action depending upon the type of sub class. Let’s assume that we have a reference of Number class and it gets assigned to an object of any class which ‘Is a’ Number. Depending on the object, we want to perform specific action (Before you all jump on me with method overriding, let me clarify that specific action here means calling specific method of impl class). This is how your code will look like:
5. Alternatives of nested if-else statements
At last, we will discuss the solution to the problem described at first (quite a stacky way to discuss). One alternative here is to divide the code into multiple methods (like isValid(), isGreater(), isBetween()) and call those methods, e.g. isBetween() may take a number and a range and return true of number falls within that range. If all the conditions are on an object of same class then another alternative is to write criteria/search method (e.g. search(), filter() etc) and pass the object to check if it meets certain criteria.
Another approach is to use Validator Pattern to validate object/parameters. Have a look at the example here. If you want to go a level deeper, then use can implement Strategy Pattern as well. Any of these approaches will make your code a lot cleaner.
Today’s blog is not about any specific
technology or framework. It explains some traits which, apart from being useful
in your day to day java programming, will also make the code cleaner, more
readable and easy to maintain. As the blog title says, it explains some ways to
skilfully avoid ‘if..else’ statements in your program. Please note that I am taking nothing away from
usefulness/importance of this control structure (remember; if-else is a control
structure and not a loop! ('Wee need to change this if loop', how many times did you say this in discussions?)) and neither do I hate if-else, however, I believe
excessive use of these makes your code look ugly and complex. And, by excessive
use, I mean more than 2 consecutive/nested if statements. Have a look at the
below code:
if(a > 0){
if(b > 0){
if(b > 1){
System.out.println("Have you
ever thought about re-evaluating your life?");
}
}else if(c > 0){
}
}
The code is not only amateur but also difficult
to debug/maintain. Imagine having to put another check in this structure as a
part of change request, nightmarish, isn’t it? Below are the examples of how to
deal with such situations.
1. Enhanced for loop: Null pointer exception
Here, I am assuming that everyone knows about
enhanced for loop (more info here).
Along with all the flexibilities it provides, there is also a catch associated
with it. If the array/collection being iterated is null then, it throws a big fat NullPointerException. There are instances where the array/collection being
supplied is retrieved at runtime and you don’t know whether it would be null.
So, a simple way to prevent that is:
List<String> names = getNames();
if(null != names){
for(String name : names){
}
}
As much as I love using enhanced for loop, I don’t
like an annoying if statement on top of every loop. I know we can’t get away
with that, so, to prevent that, I use the following approach:
/**
* Returns empty list if supplied list is
null
**/
private static <T>
List<T> safeList(List<T> input) {
if (input == null) {
return Collections.emptyList();
} else {
return input;
}
}
private void myMethod(){
List<String>
names = null;
for(String name : safeList(names)){
}
}Voila! We have just changed the place of the if statement. But the code now looks clean and if-free, doesn’t it? Below is an example of how to use it in case of an array.
/**
* Returns empty array if supplied array is
null
**/
private <T> T[]
safeArray(T[] input, Class clazz) {
if (input == null) {
return (T[])Array.newInstance(clazz, 0);
} else {
return input;
}
}
private void myMethod(){
String[]
names = null;
for(String name : safeArray(names, String.class)){
}
}As arrays need type infotmation, the methods takes class type as an argument.
2. Multiple checks of instanceof operator
Consider a scenario where you have a reference of super class and you are getting an object of one of its sub classes a runtime. Now, you want to perform specific action depending upon the type of sub class. Let’s assume that we have a reference of Number class and it gets assigned to an object of any class which ‘Is a’ Number. Depending on the object, we want to perform specific action (Before you all jump on me with method overriding, let me clarify that specific action here means calling specific method of impl class). This is how your code will look like:
private void myMethod(){
Number
number = getNumber();
if(number instanceof Integer){
//cast it and do some action
}else if(number instanceof Double){
//cast it and do some action
}else if(number instanceof BigDecimal){
//cast it and do some action
}
//And so on..
}
Imagine having more impl classes here. We will
end up with a long sequence of if-else blocks (which will of course, boil my
blood). We can’t even use switch as it doesn’t go hand in hand with instanceof operator.
What to do here then? Below is the answer:
private enum NumberType {
Integer,
Double,
BigDecimal,
Unknown;
}
/**
* Returns number type based on object
passed
**/
private NumberType
getNumberType(Number number) {
NumberType
type = null;
try {
type = NumberType.valueOf(number.getClass().getSimpleName());
} catch
(IllegalArgumentException ex) {
type = NumberType.Unknown;
}
return type;
}
private void myMethod(){
Number
number = null;
switch (getNumberType(number)){
case Integer:
//cast it and do some action
break;
case Double:
//cast it and do some action
break;
}
}
Let me explain this in brief:
We know switch works with enums. So, we are
converting the instance into enum, depending upon class type. This needs an
enum to be created with all possible class types (e.g values being class names), NumberType serves the purpose
here. Now, we need a method which would convert the object into enum type (using class name),
getNumberType() helps us in that. And finally, a switch case to distinguish
between different types and decide which action to perform.
So, we have successfully converted a lengthy
if-else structure into more readable switch case. It’s good, isn’t it? Also, if
we want to support more types in future then, all we need to do is to add a new
enum type and a case in switch.
3. Case insensitive string comparison
Well, I am not going to provide an example for
this. Java 7 included support for strings in switch case. However, there are
cases where we need case insensitive string comparison, which tempts us to fall
back on old school if else blocks with String’s equalsIgnoreCase() metod. Wait!
There is another way to stick to the switch structure. How about providing a
lower case version of string to switch (e.g. switch(name.toLowerCase()) and
creating all cases with lower case strings? It kinds of solves the purpose,
doesn’t it? This is self explanatory and hence no example.
4. Multiple Arguments in a method
This is not related to if-else structure but as
we are already discussing code and design level stuff, I thought of including
it. Have you ever come across a scenario where you need to modify the method (i.e.
to add an argument) and it already contains say 6 arguments? What will you do
in this case? Add 7th argument? Not if you are a true programmer.
There are
two questions which arise here. (i) When to decide whether a method has too many
parameters? (ii) What is the optimal number of parameters a method should have?
Answer to (i) is, when you start googling about
alternative ways to reduce method parameters, it probably has too many. And (ii)
many programming experts claim that 3 to 4 is an optimal number.
Coming back to our original scenario, what to do
if you want to add a parameter to a method which already has plenty? A pattern
called ‘Builder Design Pattern’ comes to the rescue here. It mainly consists of
creating a wrapper class containing all the parameters, creating an object of
that class and supplying that object to a method (variation of DTO pattern). If
we go in details then we may end up with another blog, so I will share a link
of the pattern and example. Click here
for more details.
5. Alternatives of nested if-else statements
At last, we will discuss the solution to the problem described at first (quite a stacky way to discuss). One alternative here is to divide the code into multiple methods (like isValid(), isGreater(), isBetween()) and call those methods, e.g. isBetween() may take a number and a range and return true of number falls within that range. If all the conditions are on an object of same class then another alternative is to write criteria/search method (e.g. search(), filter() etc) and pass the object to check if it meets certain criteria.
Another approach is to use Validator Pattern to validate object/parameters. Have a look at the example here. If you want to go a level deeper, then use can implement Strategy Pattern as well. Any of these approaches will make your code a lot cleaner.
So, this is your lot for today. Hope it will help
you at some point in your programming life. I will try to be back soon,
before the arrival of Halley’s Comet at least.
~ Au
revoir