Tuesday, May 19, 2009

Java nested classes - tips and tricks

I had to prepare this anyway, so I thought I might as well post. 

First of all to get the terminology straight: normal classes and interfaces are top-level. But class or interface can also be nested, if it is defined inside another class or interface. Nested classes originate in Beta programming language, and are available in Java since version 1.1. Non-static nested classes are called inner. Inner classes can be members (declared immediately inside outer class definitions), like this:
class Outer { class Inner { ... } }
Inner classes can also be declared within methods and other blocks, then they are called local, like:
class Outer { void foo() { class Local { ... } ... }
A more popular breed of local inner classes are anonymous classes: 
class Outer { void foo() { ... new Bar(...) { ... } ... }
For visual impression - check out this diagram.

Tip - construction: Creating new instance of nested or inner class within the outer class is simple. From outside it's a bit trickier, suppose we have a class like this:
class Outer {
   static class Nested { ... } 
   class Inner { ... } 
We can reference and instantiate the classes like this:
Outer.Nested nested = new Outer.Nested(...);
Outer out = ...  
out.Inner in = out.new Inner(...); //translated by javac to new Inner(out, ...)
The magic behind inner class constructor is that compiler implicitly adds Outer parameter to all Inner constructors, and passes the enclosing instance when constructor is invoked. From then on, inner class instance (for its entire lifetime) holds a strong reference to the enclosing instance.

Tip - instanceOf: If we have two distinct instances Outer out1, out2 then out1.Inner and out2.Inner denote the same class, but Inner instances will refer to a different enclosing instances. This is different from Scala and Newspeak, where inner class is distinct for every enclosing instance.

Tip - access enclosing instance: To access the instance of outer class from within a contained inner class use Outer.this. Nested/inner class methods/fields hide outer class ones, to access outer class elements prefix them with Outer class name, e.g. Outer.staticMethod(...) or Outer.this.anyMethod(...)

Tip - nesting and inheritance: Generally, method lookup rules in Java nested classes follow "comb semantics" - first search inheritance hierarchy, then enclosing lexical scopes. This behavior can introduce some wierd puzzlers, like #9 here. In Newspeak the enclosing scope is considered before inheritance, which makes it easier to follow from programmer's perspective.

Tip - generics: Generics type parameters of enclosing class (or method) can be used within inner classes.

Tip - interfaces: Interfaces may have nested classes (necessarily and implicitly static), it may be particurlaly useful for declaring nested enums.

Tip - statics: Inner class cannot have static declarations in it, except compile-time constants. To overcome this, static declarations canbe moved to the top-level class.
 
Trick - loading: Nested class is treated just as any other class by the JVM, e.g. it is not loaded/initialized until used. This fact is used to implement thread-safe lazy singletons using the Holder pattern.

Tip - final: Anonymous inner classes and local classes can access variables in the surrounding scope only if the variables are final:
void invokeAnon(final int number) {
   final String word = “hello”;
   someObject.pass(new Runnable() { 
      public void run() { 
         System.out.println(word.substring(number));
      }
   });
Trick - double braces: Double brace initialization is a trick of putting initialization block inside anonymous inner class declaration, like:
   final Map numbers = new HashMap(){{
      put("one", 1);
      put("two", 2);
      put("three", 3);
      //...
   }};

Trick - tokens: A cool Generics trick that uses local class to capture type parameters is super-type-token, a.k.a. Gafter Gadget. 

Tip - reflection: Starting from Java 5 a bunch of methods have been added to reflection with regards to nested classes. For example, Class#getEnclosingClass() method will let you find out the enclosing class for an inner class, for example:
class Enigma {    
 final static Class MY_CLASS = new Object(){}.getClass().getEnclosingClass();  
}  
Prolog: Last, but not least JLS is the ultimate resource for finding out more.

3 comments:

Unknown said...

Javas Double Brace Instatiation makes it a lot easier to read your source code. For review reasons is that important to you and your colleagues. I think most people only see these advantages if nobody uses them: they would be glad to have them.

A few tips and backgrounds, too: is is about the obstacles and the possibillities with Javas Double Brace Instatiation:
http://bit.ly/endUIi

I hope I could help a little.

quality writing service said...

Very useful, does not clogged and over-designed interface lets you quickly navigate through the pages of interest

periyannan said...

Amazing article
Internship providing companies in chennai | Where to do internship | internship opportunities in Chennai | internship offer letter | What internship should i do | How internship works | how many internships should i do ? | internship and inplant training difference | internship guidelines for students | why internship is necessary