Saturday, April 7, 2012

Not so final!

Last time I was reviewing a code and found out something interesting regarding the Java finally-clause. Suppose we have the code below which adds strings to a StringBuilder and returns this object.


public StringBuilder finallyReturnString() {
StringBuilder s = new StringBuilder();
s.append("init");
try {
s.append("-try");
return s;
} finally {
s.append("-finally");
}
}


It will return a StringBuilder containing “init-try-finally”. However, let’s dissemble the code above.


   0:   new     #23; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   //StringBuilder()
   7:   astore_1
   8:   aload_1
   9:   ldc     #80; //String init
   11:  invokevirtual   //StringBuilder.append()
   14:  pop
   15:  aload_1
   16:  ldc     #82; //String -try
   18:  invokevirtual   #36; //StringBuilder.append()
   21:  pop
   22:  aload_1
   23:  astore_3
   24:  aload_1
   25:  ldc     #84; //String -finally
   27:  invokevirtual   //StringBuilder.append()
   30:  pop
   31:  aload_3
   32:  areturn
   33:  astore_2
   34:  aload_1
   35:  ldc     #84; //String -finally
   37:  invokevirtual   //StringBuilder.append()
   40:  pop
   41:  aload_2
   42:  athrow


Here the try-clause is represented by the lines 15-23 and 31-32 and the finally-clause between the lines 24-30. The "finally" string is appended to the variable 1 or 3 and then “init-try-finally” contained in variable 3 is returned. You may have noticed that the finally-clause is repeated at lines 35-40.

Now let's take a similar example. Here we will not append string but set a new String each time. This will return "try" and not "finally".



public StringBuilder finallyReturnString() {
StringBuilder s = new StringBuilder("init");
try {
s = new StringBuilder("try");
return s;
} finally {
s = new StringBuilder("finally");
}
}



The disassembled code looks like:


   0:   new     #23; //class java/lang/StringBuilder
   3:   dup
   4:   ldc     #79; //String init
   6:   invokespecial   //StringBuilder(String)
   9:   astore_1
   10:  new     #23; //class java/lang/StringBuilder
   13:  dup
   14:  ldc     #65; //String try
   16:  invokespecial   //StringBuilder(String)
   19:  astore_1
   20:  aload_1
   21:  astore_3
   22:  new     #23; //class java/lang/StringBuilder
   25:  dup
   26:  ldc     #73; //String finally
   28:  invokespecial   //StringBuilder(String)
   31:  astore_1
   32:  aload_3
   33:  areturn
   34:  astore_2
   35:  new     #23; //class java/lang/StringBuilder
   38:  dup
   39:  ldc     #73; //String finally
   41:  invokespecial   //StringBuilder(String)
   44:  astore_1
   45:  aload_2
   46:  athrow

Here the the try-clause is represented by the lines 10-21 and 32-33, and the finally-clause by the lines 22-31. "init" and "try" strings are stored in variables 1 and 3. "finally" string is stored in variable 1 but variable 3 which contains "try" is returned.

This time, we will make things more clear in case you did not catch the twist; we will use String (immutable class) as a return type. The finallyReturnString() will also return "try".


public String finallyReturnString() {
String s = "init";
try {
s = "try";
return s;
} finally {
s = "finally";
}
}

The disassembled code looks like:


   0:   ldc     #75; //String init
   2:   astore_1
   3:   ldc     #61; //String try
   5:   astore_1
   6:   aload_1
   7:   astore_3
   8:   ldc     #69; //String finally
   10:  astore_1
   11:  aload_3
   12:  areturn
   13:  astore_2
   14:  ldc     #69; //String finally
   16:  astore_1
   17:  aload_2
   18:  athrow

The try-clause is represented by lines 3-7 and 11-12, and the finally-clause by the lines 8-10. Same as previous example, "init" and "try" string are stored in variables 1 and 3. "finally" string is stored in variable 1 but variable 3 which contains "try" is returned.
Next time when you have a return statement in your try-clause combined with finally-clause which affects variables, watch-out! May be you need to refactor your code!