Tuesday

Java AntiPatterns

Anti-patterns could be considered as bad programming practice. I have created a list below  that I have investigated and known. As I come to mind, I will try to add new anti-patterns.

1. The first example ensures that you can safely replace ArrayList to for example LinkedList. And you don't need to change the rest of the code. Interfaces always give more flexibility.
//USE THIS:
List<Object> object1 = new ArrayList<Object>();

//INSTEAD OF:
ArrayList<Object> object1 = new ArrayList<Object>();

2. Measure Time Intervals
javadoc says:
  - nanoTime() is a precision timer, will always produce positive.(Don't use it for Date objects.)
  - currentTimeMillis() is not a timer, it is the "wall clock". The measured time interval could be wrong or even negative.

long startTime = System.currentTimeMillis();
long elapsedTime = System.currentTimeMillis() - startTime;

//a better solution is:
long startTime = System.nanoTime();
long elapsedTime = (System.nanoTime() - startTime) / 1000000; // for ms

3. Return Collections.emptyList() instead of null or new ArrayList<T>().
  - Collections.EMPTY_LIST is not type safe.
  - (Collections.<T>emptyList()) Collections.emptyList() uses type-inference and therefore returns List<T> and it does the cast for you.

Advantages:
  - API clients won't get NullPointerException. (instead of returning null)
  - Better performance for CPU, memory wise. (instead of returning new ArrayList<T>())
  - It saves an if statement, makes code look cleaner. Your callers won't need to control null statement as below:
 if (list != null) {
  for (Item i : list) {

  }
 }

Notes:
  - Collections.emptyList() is an immutable list, a list to which you cannot add element. If you try to do some changes, you would get UnsupportedOperationException().
  - If there is semantic difference between "it is not there(null)" and "it is there but there is nothing in it(empty)" then be carefull.

4. 
Integer i = new Integer(0);
Boolean b = new Boolean(true);

//a better, faster solution also saves memory:    
Integer i = Integer.valueOf(0);
Boolean b = Boolean.valueOf(true); // or Boolean.TRUE

5.  Overrride toString method:
public class TestObject {
       private String name;
       private String old;

       public TestObject() {
       }

       public String getName() {
              return name;
       }

       public void setName(String name) {
              this.name = name;
       }

       public String getOld() {
              return old;
       }

       public void setOld(String old) {
              this.old = old;
       }

       public String toString() {
              return this.getName() + " / " + this.getOld();
       }
}
public static void main(String[] args) {
       List<TestObject> list = new ArrayList<TestObject>();

       TestObject t = new TestObject();
       t.setName("eda");
       t.setOld("1");
       list.add(t); 
           
       for(TestObject o : list) {
              System.out.println(o);
       }
}
Output:
  eda / 1

6. StringBuilder or StringBuffer?
  • StringBuffer is thread safe,synchronized but StringBuilder is not.
  • StringBuilder is faster than StringBuffer because it is not synchronized.
  • Use StringBuilder when you need a modifiable string and only one thread will access and modified this string.
  • Use StringBuffer when you need  a modifiable string and multiple threads will access and modify it.
  • Don't use StrinBuffer unnecessarily, because it takes up CPU time for locking, unlocking code.
7.   Concatenate Multiple Strings
//this solution is memory waster
String s = "";
for(Person p : personList) {
   s += ", " + p.getName();
}
s = s.substring(2);                  

//A better solution
StringBuilder sb = new StringBuilder();
for(Person p : personList) {
     if(sb.length() > 0) sb.append(", ");
     sb.append(p.getName());
}

8. Compare Strings
if (name.compareTo("John") == 0)
if (name == "John")
if (name.equals("John"))
             
//A better solution
if ("John".equals(name))//Constant comes first,so if name is null it won't get exception.
if (name.length() == 0)
if (name.isEmpty())             

//A better solution, if name is null this solution is safe.
if ("".equals(name))

9. Re-throw RuntimeException
try {
       // Code that might throw an exception

} catch(Exception e) {
       throw new RuntimeException(e);
}      
             
//A better solution
//Catch all the checked exceptions separately even if they are a lot.
//Only do this when your clients cannot recover from the problem that you are throwing checked exception. (re-throw any checked exception as RuntimeException())
//RuntimeException(e.getMessage() + ": " + yourinputs, e) --> this is the best way of passing the original exception. Put your inputs to end of messages. If generic messages would be at the beginning, tracing the logs will be easier.

try {
       // Code that might throw an exception

} catch(IOException e) {
       throw new RuntimeException(e.getMessage(), e);

} catch(ClassNotFoundException e) {
       throw new RuntimeException(e.getMessage(), e);

}

10. Catch and Log Exception
//The purpose of this catch statement are logging, it is re-throw the same exception.      
//Do this if only you know the caller doesn't log the exceptions. (especially the method is called somewhere not in your control)
   
try {
       // Code that might throw an exception

} catch(IOException e) {
    log.error(e.getMessage(), e);
    throw e;

} catch(ClassNotFoundException e) {
    log.error(e.getMessage(), e);
    throw e;
}

11. Money in floating points
Money should never be stored in a floating point data type like float, double!!!
BigDecimal d1 = new BigDecimal(2.34); 
BigDecimal d2 = new BigDecimal("2.34"); // use this!!!
System.out.println("d1: " + d1 + " d2: " + d2);

/* 
    Output:
    d1: 2.339999999999999857891452847979962825775146484375  
    d2: 2.34
*/
double d1 = 1.14 * 75; // = 85.5  but d1=85.4999...!!!!!!!!!
System.out.println("d1:: " + d1);
System.out.println("round(d1):: " + Math.round(d1)); //output=85!!!           

//The better solution:
BigDecimal d2 = (new BigDecimal("1.14")).multiply(new BigDecimal(75)); //=85.5 
System.out.println("d2:: " + d2);
d2 = d2.setScale(0, RoundingMode.HALF_UP); // 86
System.out.println("round(d2):: " + d2); // correct output: 86

/* 
   Outputs:
   d1:: 85.49999999999999
   round(d1):: 85

   d2:: 85.50
   round(d2):: 86
*/

No comments:

Post a Comment