I recently read that “A good programmer can be
as 10X times more productive than a mediocre one”, I don’t really know how true
it is; neither do I know how they arrived on 10X multiplier. Anyway, we are not
here to discuss who is a good programmer and who isn’t. We are here to discuss
some tips and tricks on java programming which can be helpful for many of us in
our daily life (finally, I am doing justice to the blog title ‘Programming
Paradigms’).
I don’t really know why I wrote the above paragraph, guess I just wanted to write something for introduction. Anyway, let’s discuss something which we are here to discuss.
Following are the examples of how a minor precaution/carelessness can save/kill you. I bet we see such code (mentioned in the below examples) at least once a day. We generally ignore it because a) it is not something which I wrote or b) the system works fine, why should I change it. My advice here would be not to ignore such instances as fixing those would take just 5 minutes compared to 5 hours when it is raised as a production bug, comes back to bite you in the arse and you have to deliver a hotfix on Saturday morning. Let’s start with the examples of do’s and don’ts now.
Till then.
I don’t really know why I wrote the above paragraph, guess I just wanted to write something for introduction. Anyway, let’s discuss something which we are here to discuss.
Following are the examples of how a minor precaution/carelessness can save/kill you. I bet we see such code (mentioned in the below examples) at least once a day. We generally ignore it because a) it is not something which I wrote or b) the system works fine, why should I change it. My advice here would be not to ignore such instances as fixing those would take just 5 minutes compared to 5 hours when it is raised as a production bug, comes back to bite you in the arse and you have to deliver a hotfix on Saturday morning. Let’s start with the examples of do’s and don’ts now.
- Avoid repetitive logging of same exception:
I am sure you must have come across code like this:
public void method1(){try{
method2();}catch(Exception e){//Handle exception}
}public void method2() throws Exception{try{method3();}catch(Exception e){log.error("Error in method 2", e);throw e;}}public void method3() throws Exception{try{//Some code}catch(Exception e){log.error("Error in method 3", e);throw e;}}
Although this code may just work fine, it’s not something which you will like. E.g. if any exception is thrown from method 3, it is logged twice before reaching to actual handler code (which may log it one more time to complete the hat trick), filling the log files with unnecessary stack traces and making debugging difficult.
Best practice here would be to remove try-catch blocks from method2 and method3 (they can have try-finally pair if they are acquiring/releasing resources) and place logging and exception handling in method1 only. - Don’t swallow exceptions in catch:
This scenario is a variant of what we just discussed. Have a look at the below code:
public void method2(){try{
String result = method3();//do further processing}catch(Exception e){//Handler code}
}
public void method3(){
String result = null;try{//Some code}catch(Exception e){log.error("Error in method 3", e);}return result;}
Here, method3() catches the exception and just logs it. Method2() has no idea of what happened in method3() and will continue its normal flow, causing a NullPointerException itself.
Again, best practice here for method3() would be to avoid dealing with exception and let it propagate to the caller. - For String equality check (i.e. using equals()
and equalsIgnoreCase()), always keep the constant value on the left hand side:
Consider the below scenario:
public void process(){String success = "Success";
String failure = "failure";String status = getStatus();if(success.equalsIgnoreCase(status)){//...}else if(status.equalsIgnoreCase(failure)){//Don't do this//...}}
Both the comparisons would work perfectly fine as long as getStatus() returns a not null status. However, we shouldn’t rely on it returning a not null status every time (what if it is designed like method3() of our example 2? You never know!). As both a.equals(null) and a.equalsIgnoreCase(null) return false for every String a, it is always best to use constants on the left hand side. - Don’t return null collection from any method:
It is always best to return an empty collection rather than null while writing any method which returns a collection. While developing a method, we don’t know who else will be using that method, they may be calling Collection.size() on returned object. Consider the example below:
public List<String> getNames(){List<String> names = new ArrayList<String>();
try{//...
}catch(Exception e){names = null;//Don't do this}return names;}
public void processNames(){List<String> names = getNames();log.debug("Found " + names.size() + " names to process");//Boom!}
As we can see, processNames() always assumes that the list will never be null. Instead of forcing null checks for all the caller methods (after they see NullPointerException in production logs), it is better to return an empty list beforehand. - Don’t reinvent the wheel, use open source
libraries:
Continuing from point 4, let’s say you are calling such method (which return a collection) at multiple places and you are not sure whether it will always be not null. In this case, to compute the size (or to treat a null list as an empty list), you will end up writing something like this:
public int getSize(List<String> list){if(null == list){return 0;}else{return list.size();}}
A little bit of google before writing should say that there’s already a library (apache commons) which contains hundreds of such methods to handle null and empty collection (more on that here). With the help of this, the above method will be transformed into following:
CollectionUtils.size(list);
This is just an example. There are plenty of such libraries and methods to ease the coding effort. I would say that one should always google at least once before writing any utility as there are high chances that the utility already exists.
Till then.