Sunday, March 10, 2013

Inner classes in Java

Java provides a mechanism through which you can declare one class inside another class. It's known as Inner class or Nested class.

          class OuterClass{   //Enclosing class
                  class InnerClass{   } //Inner class
          }

So, here, we are dealing with class definition within another class. Inner class is a member of the enclosing class. Now obvious questions are : why do we need it ? when do we need to use it ? Is it same as composition ?

Let's understand different aspects of inner classes before addressing these questions:

case 1 : Normal/Plain Inner class (Nested class)

public class Outer{
        private String id;

        private class Inner{
            private String name;
            public void print(){
                System.out.format(" name :"+ this.name + " ;id: "+ id);
            }
        }
        public static void main(String[] args){
            Outer oc = new Outer();
            oc.id = "id1";
         
            Outer.Inner ic = oc.new Inner();
            ic.name = "rai";
          
            ic.print();  //prints name :rai ;id: id1
        }
    }
Above class is simplest possible way to see Inner class into action. Let's note few points about code :
  1. Normal classes (non-inner) cannot be made private or protected (can only have public and package access) but inner classes can even use private access modifier as shown above. Making inner class private enables you to completely hide details about its implementation.
  2. Inner class can only be accessed by its outer class.
  3. If you want to invoke inner class from a static method (like main method); you need to do as shown in main method i.e. create an instance of outer class first and then through it create inner class instance as OuterClass.InnerClass obj = <OuterClassInstance>.new InnerClass();
  4. Note, print method; you are able to directly access id attribute of outer class. This is possible because inner class instance is associated with an outer class instance (Outer.Inner ic = oc.new Inner();)

Above case, inner class instance is created inside static method of outer class; lets try to do the same inside a normal method of outer class:

public class Outer {
    public void setInner() {
        Inner i = new Inner();
        i.name = "rai";
        i.print();
    }

    private class Inner {
        private String name;

        public void print() {
            System.out.println(" name :" + name);
        }
    }

    public static void main(String[] args) {
        Outer oc = new Outer();
        oc.setInner();
    }
}
 So when you try to instantiate an inner class from the non-static method of outer class; you just need to create instance in normal way. This makes sense as setInner method is called by an instance of outer class; so you are creating inner class instance linked with an outer class instance.

Now, what if, Outer class as well as Inner class has same attribute named as id. How to access both inside inner class?

public class Outer {
    private String name;
   
    private class Inner {
        private String name;

        public void print() {
            System.out.println(" Inner name :" + this.name);
            System.out.println(" Outer Name :"+ Outer.this.name);
        }
    }
}

case 2 :  static Inner class or static Nested classes

Now let's analyze how a static inner class will behave and how can we access it? (need to brush up static keyword concepts? this tutorial). Static inner class is commonly referred to as static nested class. Objects of normal inner classes (non-static) implicitly keeps a reference to the object of the enclosing (Outer) class that created it. This is not true for static nested classes.

So when you don't need a connection between the inner class object and the outer class object, then you can declare the inner class as static. This means that you don't need an outer class object to instantiate inner class. And you can't access a non static outer class object from an object of a nested class.

public class Outer {
    private String name;
   
    private static class Inner {
        private String name;

        public void print() {
            System.out.println(" Inner name :" + name);
            //won't compile
            //System.out.println(" Outer Name :"+ Outer.this.name); 
        }
    }
   
    public static void main(String args){
        Inner i = new Inner();
        i.print();
    }
}
Static nested classes don't have any link with the outer class; so it is analogous to a static method.

case 3 :  Inner classes in methods/scopes

This is one of the trickiest and confusing aspect of inner classes; you can declare static classes inside body of a method or in a block. There are two possibilities :
  1. local classes : declare an inner class within body of method with name
  2. anonymous classes : declare an inner class within body of method without naming it
  local class : Below example shows a validation example. It checks if a string representation of number is even or odd.
public class Outer{
    public static void ifEven(String number) {
       final String message = "Even Test !";  //note it's final
       class EvenValidator {
            int val = 0;
            EvenValidator(String number){
                val = Integer.parseInt(number);
            }
            public boolean isEvenNumber() {
                System.out.println(" message : "+ message);
                return val % 2 ==0 ? Boolean.TRUE : Boolean.FALSE;
            }
            //static component not allowed
            /*public static void f(){
                System.out.println("hi");
            }*/
         }
        EvenValidator myNumber1 = new EvenValidator(number);
        System.out.print(myNumber1.isEvenNumber());
    }
    public static void main(String... args) {
        ifEven("123");
    }
}
  Output
      message : Even Test !
     false

Please note that local class can only access local variables and parameters of the enclosing block that are final (hence message is final). Also you can't declare or define any static members, as evident from commented static method f(). So local classes are like inner classes.

anonymous class : This takes local classes one step further by enabling you to declare and instantiate a class at the same time. So anonymous classes are like local classes except that they don't have a name.

interface Contents{
    int getValue();
}
public class Anonymous {  
    public Contents contents(final String message, int tmp){
        return new Contents(){
            private int i=10;
            public int getValue(){
                System.out.println(" message :"+ message);  //final is must
                return i;
            }
        };
    }
    public static void main(String[] args) {
        Anonymous a = new Anonymous();
        Contents c = a.contents("hi");
        System.out.println(" val :"+ c.getValue());
    }
}
Output:
 message :hi
 val :10

 Points which need to be noted :
  1. The semicolon at the end of the anonymous inner class marks  the end of the expression.
  2. If you're defining an anonymous inner class and want to use an object that's defined outside the anonymous inner class, then the compiler requires that the argument reference be final. This is why message is declared as final.
  3. You can't have a named constructor in an anonymous class as there's no name.
  4. tmp parameter doesn't need to be declared as final as it's not getting used anywhere in the code.

Let's see a more familiar example which uses Runnable interface to create threads.
public class Anonymous {  
    public Thread thread(){
        Runnable r = new Runnable(){
            public void run(){
                System.out.println("run");
            }
        };
        Thread t = new Thread(r);
        return t;
    }
    public static void main(String[] args) {
        Anonymous a = new Anonymous();
        Thread thread = a.thread();
        thread.start();
    }
}

case 4 :  Inner class inside interfaces

Normally you can't put any code inside an interface, but a nested/inner class can be part of an interface. Any class you put inside an interface is automatically public and static. So you can nest a class inside an interface when you want to create some common code to be used with all different implementations of that interface.
 interface ClassInInterface {
    class Dummy{ //static class
        public void sayHi(){
            System.out.println("Hi !");
        }
    }
}
public class ClassInsideInterface implements ClassInInterface{
    public static void main(String[] args){
        Dummy d = new Dummy();
        d.sayHi(); 
//prints Hi !
    }
}

.class files

Unlike normal classes which generates a single .class file; compiling a class which has inner classes generates separate class files. One class for outer class and one for each inner class. Below are two possibilities :
  1. If inner class has a name ( like in case 1 and case 2): Outer.class and Outer$Inner.class.
  2. If inner class is NOT named (i.e. anonymous inner class) : Anonymous.class and Anonymous$1.class 

why ?

  1. Inner class provides a view to the outer class, as it usually manipulates the outer class object that it was created within.
  2. Inner class can independently extend a class (or implement an interface). So it solves multiple inheritance problem of Java. Interfaces to an extent solve this problem by allowing you to implement multiple interfaces. But inner classes actually allow multiple implementation inheritance
  3. Is it same as composition ? big NO
---
do post your feedback !!!

No comments:

Post a Comment