Saturday, February 1, 2014

Tricky aspects of Anonymous Classes in Java

I have discussed briefly about anonymous classes in my earlier Inner class post. Some of the aspects of Anonymous classes are confusing which require more elaborate explanation. And few aspects are not well known to most of the programmers. So, I am revisiting these aspects of Anonymous class in detail here.

[Brush up Inner Class and Anonymous class basics - here]
[Java 7 Specification of Anonymous class - here]

As a refresher; Anonymous classes are declared inline inside another class, and hence they are inner as well. So we can call them as Anonymous class or Anonymous Inner class. Also, Anonymous class can never be static and abstract and they are implicitly final [Java Specification]. 


Compact and Efficient Code

If you use Anonymous classes properly, you can reduce the number of lines of code. Check out below example. It shows three styles of creating an instance of thread using Anonymous class.

 public class AnonymousSample {  
      public static void main(String[] args) {  
   
           // first approach  
           Runnable r = new Runnable() {  
                public void run() {  
                     System.out.println("run !");  
                }  
           };  
           Thread t = new Thread(r);  
           t.start();  
   
           // second approach  
           Thread tt = new Thread(new Runnable() {  
                public void run() {  
                     System.out.println("run !");  
                }  
           });  
           tt.start();  
   
           // third approach  
           new Thread() {  
                public void run() {  
                     System.out.println("run !");  
                }  
           }.start();  
      }  
 }  

All the above approaches achieve the same thing, but clearly, the third approach is much more compact and cleaner way. 

Can't have an explicit constructor

Yes, Anonymous class can't have a constructor.
There is a workaround, though. You can declare a local final variable and declare an instance initialization block as shown below:

 public class AnonymousConstructor {  
      public static void main(String[] args) {  
           final int attribute;  
           Object obj = new Object() {  
                {  
                     attribute = 20;  
                     System.out.println("arg = " + attribute);  
                     method();  
                }  
   
                public void method() {  
                     System.out.println("inside method");  
                }  
           };  
      }  
 }  


But, there is still a problem.
The variable attribute can't be modified inside the inner class (compiler forces you to make it final). So, we haven't been able to truly initialize/construct the variable inside initialization block. 

But there is a hack for it as well!
Java treats final objects and final primitives differently.

When an object is declared as final, its reference can't be changed but you can change the value contained in it. In below example, I have created a dummy array to contain the primitive value. (Remember that in Java array is also an object).

 public class AnonymousConstructor {  
      public static void main(String[] args) {  
           final int attribute[] = { 10 };  
           Object obj = new Object() {  
                {  
                     attribute[0] = 20;  
                     System.out.println("arg = " + attribute[0]);  
                     method();  
                }  
   
                public void method() {  
                     System.out.println("inside method");  
                }  
           };  
      }  
 }  
Bingo!!! So you can get around the limitation of no explicit constructor.

Double Brace Initialization

This is one of the unknown features of Java. It comes in handy to initialize Collection APIs (List, Set, Map) in a very compact manner. 

 import java.util.ArrayList;  
 import java.util.List;  
   
 public class DoubleBraceInitialization {  
      public static void main(String[] args) {  
           List<String> list = new ArrayList<String>() {  
                {  
                     add("abc");  
                     add("def");  
                }  
           };  
   
           System.out.println(" list is " + list);  
      }  
 }  

Output : list is [abc, def]
Note that, the second brace is instance initialization brace.
Want to read more about it? link

Can't access method outside class

Let's start with a problem directly to stress a point. What will be the output of the below code?

 import java.util.concurrent.Callable;  
 interface Sample{  
   public void sampleMethod();       
 }  
   
 public class AnonymouseMethodInvocation {  
      public static void main(String[] args) {  
           new Sample(){  
                public void sampleMethod(){  
                     System.out.println("inside sample method");  
                }  
                public void method(){  
                     System.out.println("sample method");  
                }  
           }.method();  
          
             
           Callable<String> c = new Callable<String>(){  
                @Override  
                public String call() throws Exception {  
                     return "hello";  
                }            
           };  
      }  
 }  

It won't compile.
Surprised???

The compiler will not allow you to call method m().
When you say

      Sample s = new Sample(){  };


Means that, Create an object of an Anonymous class that's inherited from class Sample.

The reference returned by the new expression is automatically upcast to a Sample reference.

So using superclass reference, you can call method contained in the base class, but not method added in subclass. Here, method m() is added in the anonymous class but sampleMethod() is from the base class itself. So you can call sampleMethod() but not method m().

---
do post your comments/questions below !!!

No comments:

Post a Comment