Monday, December 26, 2016

What are nested and inner classes in java

Definition:

A nested or inner class is a class defined inside another class as a data member. In java we can have 4 types of nested classes namely:
  1. static inner class
  2. non static inner class
  3. local inner class
  4. Anonymous inner class
1. static inner class:
A static inner class or its method can only refer to the static data member or static methods of its outer class. We cannot declare a normal class as static, only inner classes is allowed to be static. Let’s compile and run this program, where we are creating an outer class and a static inner class inside the outer class.

File Name: Outer.java

package com.dev21century;

public class Outer {

      int x = 10;
      static int y = 20;
      static class Inner
      {
        void show()
        {
         System.out.println("x = " + x);//Line No 11 - compilation error
         System.out.println("y from show = " + y);
        }
      }
      public static void main(String args[])
      {
            System.out.println("y from main = " + y);
            Inner inner = new Inner();
            inner.show();
      }
}

In above program there is a compiler error at line number 11, because a method of static inner class cannot access non static data member of its outer class. Now, remove line number 11 and recompile & run again, the output will be as follows:


In above program compiler will create two separate .class files with name:
Outer.class
Outer$Inner.class

Quick question – is it breaching the java rule that a .class file name should be same as class name?
Answer – No, because the .class file name is same as its class name, compiler is just prefixing something to it.

Quick Question – can we keep main() in static nested class?
Answer – Yes, we can but there is no use. In above just program try to make show() method as static and call like this – Inner.show(). It will run fine.

Rule – static nested class can be made self-executable by adding a main() method in it.

Quick Question – can we create object of inner class outside its outer class, if yes then there is no use of nested class?
Answer – yes, we can create object of inner class outside of its outer class. For example:

Create a Temp.java file inside dev21century package:

package com.dev21century;
class StaticOuter {
      int x = 10;
      static int y = 20;
      static class Inner
      {
        void show()
        {
         //System.out.println("x = " + x);//Line No 11 - compilation error
         System.out.println("y from show = " + y);
        }
      }
}

public class Temp {
      public static void main(String args[])
      {
        System.out.println("Outer.y = " + StaticOuter.y);
        //creating object of inner class outside
        StaticOuter.Inner inner = new StaticOuter.Inner();
        inner.show();
      }    
}

Output:

Now, if java allows to access inner class from outside then is it a security breach, because we are allowing the data member (inner class) to access from outside world.
Yes, it is. The solution is to make the inner class as private.

Inheritance in inner class:
When we inherit an outer class then as per the inheritance rule, all of its public / protected data members including inner class are accessible to the sub class. For example:

Alter above example as follows (Temp.java):

package com.dev21century;
class StaticOuter {
      int x = 10;
      static int y = 20;
      protected static class Inner
      {
        protected void show()
        {
        //System.out.println("x = " + x); //Line No 11 - compilation error
        System.out.println("y from show = " + y);

        }
      }
}

public class Temp extends StaticOuter {
      public static void main(String args[])
      {
        System.out.println("Outer.y = " + StaticOuter.y);
        //creating object of inner class outside
        
        Inner inner = new Inner();
        inner.show();
      }    
}

Output:


We can also inherit the inner class directly as follows (Temp.java):

package com.dev21century;
class StaticOuter {
      int x = 10;
      static int y = 20;
      protected static class Inner
      {
        protected void show()
         {
        //System.out.println("x = " + x); //Line No 11 - compilation error
        System.out.println("y from show = " + y);
         }
      }
}

public class Temp extends StaticOuter.Inner {
      public static void main(String args[])
      {
        System.out.println("Outer.y = " + StaticOuter.y);
        //creating object of inner class outside
        Temp temp = new Temp();
        temp.show();    
      }    
}

Output:
Same as above.

2. Non static class:
Now, make everything non static in above program (TempNonStatic.java):

package com.dev21century;
class NonStaticOuter {
      int x = 10;
      static int y = 20;
      class Inner
      {
          void show()
          {
           System.out.println("x = " + x);
           System.out.println("y from show = " + y);
          }
      }
}

public class TempNonStatic {
      public static void main(String args[])
      {
        NonStaticOuter outer = new NonStaticOuter();
        System.out.println("outer.x = " + outer.x);
           
        NonStaticOuter.Inner inner = outer.new Inner();
        inner.show();    
      }    
}

Output:


In above program, as we know inner class is a non static class, so we must have to create an object before accessing its properties. So, the syntax to create such object is:
NonStaticOuter.Inner inner = outer.new Inner()

Rule- a non static nested class can access all data members and methods of its outer class.

Inner class is not extending outer and not creating any object of it then how inner is able to access x variable of outer class?

Because there are two ways for accessing non static data member of other class.

Data Shadowing in Inner class:

Let’s assume that there is a local variable x inside Inner class in above program, then priority always goes to the local variable. So, let’s discuss how we can access the data member of outer class. The way is: “Outer.this.x”

Here is the full-fledged program (TempNonStatic.java):

package com.dev21century;
class NonStaticOuter {
      int x = 10;
      static int y = 20;
      class Inner
      {
        int x = 30;
        void show()
        {
         System.out.println("local inner x = " + x);
         System.out.println("outer x = " + NonStaticOuter.this.x);
         System.out.println("y from show = " + y);
        }
      }
}

public class TempNonStatic {
      public static void main(String args[])
      {
        NonStaticOuter outer = new NonStaticOuter();
        System.out.println("outer.x = " + outer.x);
           
        NonStaticOuter.Inner inner = outer.new Inner();
        inner.show();
      }    
}

Output:


Rule – non static nested class cannot have its personal static data members or static methods. Which means the main() method cannot be added in non static inner class hence a non static inner class cannot be made as self executable.

Inheritance of non static class – we can extend the outer class but its inner class cannot be inherited in sub class.

3. Local Inner Class:

Local in the sense something which is defined inside an init block, a static block, a method or a constructor. All rules and regulations do apply here as well and it cannot go beyond its block.

Rule – when we are using local variables of a function within local class then it must be final.

For example: (TempLocalClass.java)
package com.dev21century;

public class TempLocalClass {
int x=10;
      static int y=20;
      void display()
      {
            final int p=30;
            class Inner
            {
                  public void show()
                  {
                        System.out.println("p = "+p);
                        System.out.println("x = "+x);
                        System.out.println("y = "+y);
                  }
            }
            Inner inner=new Inner();
            inner.show();
      }    
      public static void main(String[] args) {
            TempLocalClass temp=new TempLocalClass();
            temp.display();
      }
}

Output:


In above program try to remove final keyword and recompile, it will not be compiled successfully.

In above program we have invoked the show() method inside display method, now how we will invoke it outside its method, because we cannot create an object of local class outside of the method. The solution for this problem is, create an interface with show() method and implement it in local class and return its reference variable. Like this:

Alter above program as follows:
package com.dev21century;
interface My
{
      public void show();
}

public class TempLocalClass {
      int x=10;
      static int y=20;

      My display()
      {
            final int p=30;
            class Inner implements My
            {
                  public void show()
                  {
                        System.out.println("p = "+p);
                        System.out.println("x = "+x);
                        System.out.println("y = "+y);
                  }
            }
            My my = new Inner();
            return my;
      }    
      public static void main(String[] args) {
            TempLocalClass temp = new TempLocalClass();
            My my = temp.display();
            my.show();
      }
}

Output:
Same as above.

Here compiler will create separate .class files for all local classes and name them something like Outer$1InnerClassName.class, Outer$2InnerClassName.class etc. In our program the compiler will create .class file name as TempLocalClass$1Inner.class.

4. Anonymous Class:
An anonymous class is a temporary Class & Object, it is mandatory that an anonymous class must have its parent, we always keep its reference to its parent.

Lets define an anonymous class in 3 different ways:

1st Way: (create a file AnonymousDemoImpl.java inside com.dev21century):

package com.dev21century;
interface AnonymousDemo
{
      public void show();
}

public class AnonymousDemoImpl {
     
      int x = 100;
      static int y = 200;
      AnonymousDemo display(){
            return (new AnonymousDemo(){
           
            public void show()
            {
                  System.out.println("x = " + x);
                  System.out.println("y = " + y);
            }});
      }
     
      public static void main(String[] args) {
            AnonymousDemoImpl anonymous = newAnonymousDemoImpl();
            AnonymousDemo anonymousInterface = anonymous.display();
            anonymousInterface.show();
      }
}
Output:


In above program we have created an anonymous class inside display method, and there is no way to get object of that class in main() method. That’s why we have created an interface (AnonymousDemo), and made our anonymous class as a child of that interface. In main() method we have called the display method which returns object of anonymous class in a reference variable of interface.

Here compiler creates separate .class files for all anonymous classes and name them like Outer$1.class, Outer$2.class etc. In our program the .class file name is AnonymousDemoImpl$1.class.

2nd Way to create anonymous class:

Alter above program as follows:

package com.dev21century;
interface AnonymousDemo
{
      public void show();
}

public class AnonymousDemoImpl {

      int x = 10;
      static int y = 20;
      void display(AnonymousDemo annoInterface)
      {
            annoInterface.show();
      }
     
   public static void main(String[] args) 
    {
     AnonymousDemoImpl anonymous = new AnonymousDemoImpl();
     anonymous.display(new AnonymousDemo()
      {
       public void show()
       {
        System.out.println("Inside show method of anonymous class.");
        System.out.println("y = " + y);
       }
       });
    }
}

Output:


In above program we have created the anonymous class in our main method itself, and inside display method we are only calling show() method of interface which is implemented inside our anonymous class.

3rd Way to create anonymous class:

package com.dev21century;
interface AnonymousDemo
{
      public void show();
}

public class AnonymousDemoImpl {

      public static void main(String[] args) 
         {
          AnonymousDemo anonymous = new AnonymousDemo(){
          public void show()
           {
            System.out.println("Inside anonymous class.");
           }
           };
            anonymous.show();
      }
}

Output:
Inside anonymous class.

Above program is very easy way of implementing anonymous class. In all of the above programs, it is looking like we are creating an object of interface, but it is clearly not. We can never create instance of any interface. We are just creating a reference variable of interface and assigning anonymous class object in it.

Classes within an interface:
There is an option in java to create classes within an interface. In this case the inner class should always be the static class. We cannot create a non static class inside an interface.

Another condition is, we cannot create local class because we have to define the local classes in a method, and as we know we cannot define any method inside an interface.

Let’s take this example:

Create a file ClassInInterfaceImpl.java inside com.dev21century package:

package com.dev21century;
interface MyInterface1{
      public void show();
}

interface MyInterface2{
  MyInterface1 myinterface1 = new MyInterface1()
  {
   public void show()
    {
      System.out.println("Inside anonymous class of interface.");
    }
   };
    
 class Inner{
    void display()
      {
        System.out.println("static nested class inside interface.");
      }
     }
}

public class ClassInInterfaceImpl implements MyInterface2
//extends MyInterface2.Inner{

      public static void main(String[] args) {
            myinterface1.show();
            Inner inner = new Inner();
            inner.display();
            //new ClassInInterfaceImpl().display();
      }
}

Output:


In above program we have created an anonymous class as well as an inner class inside the interface.

No comments:

Post a Comment