Nested Classes
  • Classes that are declared inside the body of a class are called "nested classes".
  • The following are the main reasons behind the concept of nested classes in Java:
    • Grouping of logical classes
      • When designing a class, we always try to create well-focused classes - with a unique behavior and purpose. Let us imagine we have designed classes A and B on the same principle. Later we found, class B can only be used by class A. In such cases, we can simply put class B inside class A.
    • Encapsulation
      • By writing nested classes, we have hidden them from the world and made them available only to their enclosing class.
    • Maintainability and re-usability of code
      • Encapsulation brings the beauty of maintainability of code. In our earlier example, we can write another class B which is visible t o the entire world. This has nothing to do with the one already present inside class A.
  • Nested class is of 2 kinds:
    • Inner class
    • Static nested class
  • Inner class is of 3 types:
    • Inner class
    • Method-local inner class
    • Anonymous inner class
  • Nested class behaves just like any other member of its enclosing(outer) class.
  • It has access to all the members of its enclosing class.

Inner Class

  • We define the term "inner class" to the nested class that is:
    • declared inside the body of another class.
    • not declared inside a method of another class.
    • not a static nested class.
    • not an anonymous inner class.
  • An example:
class Outer{
    class Inner{
    }
}
  • When we compile the above code we get 2 class files:
    • Outer.class
    • Outer$Inner.class
  • Notice that inner class is tied to its outer class though it is still a separate class.
  • An inner class cannot have any kind of static code including the public static void main(String[] args).
  • Only classes with "public static void main(String[] args)" can be called using "java" command.
  • In our earlier example, Inner class didn't have a static main method. So, we can't call java Outer$Inner!
  • The inner class is just like any other member of its enclosing class.
  • It has access to all of its enclosing class' members including private.

Instantiating an Inner Class

  • Since inner class can't stand on its own, we need an instance of its outer class to tie it.
  • There are 2 ways to instantiate an instance of inner class:
    • From within its outer class
    • From outside its outer class
class Outer{
    private String s = "Outer string"; //Outer instance variable
    Inner i1 = new Inner();
    void getS(){
        System.out.println(s); 
    }
    void getInnerS(){
        System.out.println(i1.s);
    }
    class Inner{
        private String s = "Inner string"; //Inner instance variable, uninitialized
        void getS(){
            System.out.println(s);
        }
        void getOuterS(){
            System.out.println(Outer.this.s);
        }
    }
    public static void main(String[] args){
        Outer o = new Outer();
        Outer.Inner oi = o.new Inner();//can also be new Outer().new Inner();
        o.getS();
        oi.getS();
        o.getInnerS();
        oi.getOuterS();
    }
}
Output:
Outer string
Inner string
Inner string
Outer string
  • Another excellent example:
class InnerTest{
    public static void main(String[] args){
        Outer o = new Outer();
        Outer.Inner i = o.new Inner();//one way
        i.seeOuter();
        i = new Outer().new Inner();//another way
        i.seeOuter();
    }
}
class Outer{
    private String s = "outer s";
    void makeAndSeeInner(){
        System.out.println(this);//refers to Outer.this
        Inner i = new Inner();//No need of Outer.this explicitly, because, Outer.this already exists here.
        i.seeOuter();
    }
    void seeInner(){
        System.out.println(s);//How to see Inner s here? You can't, because Inner.this not present.
    }
    strictfp class Inner{
        private String s = "inner s";
        void seeOuter(){
            System.out.println(this);
            System.out.println(Outer.this.s);//Need to mention Outer because this refers to Inner.this here.
            System.out.println(s);//Or, this.s
        }
    }
    abstract class AbInner{
        private String s = "abstract inner s";
        void seeOuter(){
            System.out.println(this);//this refers to the subclass not the abstract class.
            System.out.println(Outer.this.s);
            System.out.println(s);
        }
        abstract void abMethod();
    }
    class Some extends AbInner implements Runnable, Animal{//can extend and implement
        public void run(){}
        void abMethod(){
            System.out.println(this);
            System.out.println(super.s);
        }
    }
    public static void main(String[] args){
        Inner i = new Outer().new Inner();
        //Inner i = new Inner();//can't exist w/o outer class instance
        i.seeOuter();
        Outer o = new Outer();
        o.makeAndSeeInner();
        o.seeInner();
        //new Outer().makeAndSeeInner();
        Some so = new Outer().new Some();
        so.seeOuter();
        so.abMethod();
    }
}
interface Animal{
}

Run it and check your output. How to run? Look into the code and you'll definitely understand how to do that. :)

Method-Local Inner Classes

  • A method-local inner class is defined within a method of the enclosing class.
  • For the inner class to be used, you must instantiate it, and that instantiation must happen within the same method, but after the class definition code.
  • A method-local inner class cannot use variables declared within the method (including parameters) unless those variables are marked final.
  • The only modifiers you can apply to a method-local inner class are abstract and final. (Never both at the same time, though.)

Anonymous Inner Classes

  • Inner classes that are declared wtihout a name are called anonymous inner classes.
  • Anonymous inner classes have no name, and their type must be either a subclass of the named type or an implementer of the named interface.
  • An anonymous inner class is always created as part of a statement; don't forget to close the statement after the class definition with a curly brace. This is a rare case in Java, a curly brace followed by a semicolon.
  • Because of polymorphism, the only methods you can call on an anonymous inner class reference are those defined in the reference variable class (or interface), even though the anonymous class is really a subclass or implementer of the reference variable type.
  • An anonymous inner class can extend one subclass or implement one interface, Unlike non-anonymous classes (inner or otherwise), an anonymous inner class cannot do both. In other words, it cannot both extend a class and implement an interface, nor can it implement more than one interface.
  • An argument-local inner class is declared, defined, and automatically instantiated as part of a method invocation. The key to remember is that the class is being defined within a method argument, so the syntax will end the class definition with a curly brace, followed by a closing parenthesis to end the method call, followed by a semicolon to end the statement: });

Static Nested Classes

  • Nested classes that are declared "static" are called static nested classes.
  • Static nested classes are inner classes marked with the static modifier.
  • A static nested class is not an inner class, it's a top-level nested class.
  • Because the nested class is static, it does not share any special relationship with an instance of the outer class. In fact, you don't need an instance of the outer class to instantiate a static nested class.
  • Instantiating a static nested class requires using both the outer and nested class names as follows:
BigOuter.Nested n = new BigOuter.Nested();
  • A static nested class cannot access non-static members of the outer class, since it does not have an implicit reference to any outer instance (in other words, the nested class instance does not get an outer this reference).
  • An example:
class StaticOuter{
    String a = "StaticOuter string";
    static String b = "StaticOuter static string";
    void seeStaticInner(){
        //System.out.println(nonstatic);//cannot find symbol
        //System.out.println(StaticInner.nonstatic);//nonstatic is not static to access like this!
        System.out.println(new StaticInner().nonstatic);//OK
        System.out.println(StaticInner.s);//OK, s is static
    }
    public static void main(String[] args){
        //System.out.println(s);//Doesn't compiles without writing the exact location of s
        System.out.println(StaticInner.s);
        StaticOuter so = new StaticOuter();
        so.seeStaticInner();
    }
    static class StaticInner{
        String nonstatic = "StaticInner nonstatic string";
        static String s = "StaticInner static string";
        public static void main(String... args){
            //System.out.println(nonstatic);//cannot be referenced from a static context
            System.out.println(s);
            System.out.println(b);//OK, b is a static string. But not 'a' which is non-static!
        }
    }
}
class SomeOther{
    public static void main(String[] args){
        System.out.println(StaticOuter.StaticInner.s);//Write the exact location of s
        StaticOuter.StaticInner si = new StaticOuter.StaticInner();//To access nonstatic members we need an object or 'this'
        System.out.println(si.nonstatic);//No 'this' exists in static context!
        System.out.println(si.s);//'si' is simply a fake! To the end it will be StaticOuter.StaticInner!
    }
}
Output:
$java StaticOuter
StaticInner static string
StaticInner nonstatic string
StaticInner static string
 
$java StaticOuter$StaticInner
StaticInner static string
StaticOuter static string
 
$java SomeOther
StaticInner static string
StaticInner nonstatic string
StaticInner static string
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Share Alike 2.5 License.