Nested Classes

A class defined within another class or interface is called a nested class. A nested class can be either static or non-static. A non-static nested class can also be called as inner classes if it is defined outside of any code block. A static class defined within another class is simply called a static nested class.

Inner Class

  • As discussed above, a non-static class defined within a class but outside of any code block is called as Inner class
package com.techstackjournal.nested.ex1;

public class OuterClass {
	public class InnerClass {

	}
}
  • When you compile the above file, it will create 2 classes, OuterClass.class and OuterClass$InnerClass.class
  • You can access inner class within the outer class just like any other class you do normally.
InnerClass ic = new InnerClass();
  • However, if you want to access inner class outside of the outer class, you need to use the outer class reference variable or object followed by a dot operator and the new operator.
package com.techstackjournal.nested.ex1;

public class Application {

	public static void main(String[] args) {
		OuterClass oc = new OuterClass();
		OuterClass.InnerClass ic = oc.new InnerClass();
		new OuterClass().new InnerClass();
	}

}
  • An outer class can also be called an enclosing class
  • An inner class object is always tied with the outer class object. There is no existence of an inner class object without an outer class object
  • An inner class can access all levels of the outer class instance members
package com.techstackjournal.nested.ex1;

public class OuterClass {

	private String privateVariable = "privateVariable of outer class";
	String defaultVariable = "defaultVariable";
	public String publicVariable = "publicVariable";
	protected String protectedVariable = "protectedVariable";
	private InnerClass ic = new InnerClass();

	public class InnerClass {
		private String privateVariable = "privateVariable of inner class";
		
		private static final String CONSTANT_VARIABLE="CONSTANT VARIABLE";
		
		public InnerClass() {
			System.out.println(privateVariable);
			System.out.println(OuterClass.this.privateVariable);
			System.out.println(defaultVariable);
			System.out.println(publicVariable);
			System.out.println(protectedVariable);
			System.out.println(CONSTANT_VARIABLE);
		}

	}
}
package com.techstackjournal.nested.ex1;

public class Application {

	public static void main(String[] args) {
		new OuterClass();
	}

}
privateVariable of inner class
privateVariable of outer class
defaultVariable
publicVariable
protectedVariable
CONSTANT VARIABLE
  • An outer class cannot directly access the instance variables of the inner class, it has to create an instance of the inner class to use it
  • The inner class can be defined with any access level just like the instance variable
  • this keyword within an inner class refers to the current inner class object
  • If the outer class and inner class have members with the same name, we say that inner class members are shadowing the enclosing class members. You can refer outer class shadowed member with the outer class name followed by this keywords: OuterClass.this.member
  • Instance variables within an inner class cannot be declared as static unless initialized with a constant expression using final keyword
  • Methods cannot be declared static inside an inner class; static methods can only be declared in a static or top-level type class

When to use an Inner Class?

  • Inner classes are most frequently used when you are working with AWT/Swing components
    • In AWT/Swing, if you want to perform some action when a user clicks on a button, you need to register a listener with the button
    • A listener is just another class that actually implements a listener class, for example, an ActionListener class, providing what action to be performed in a method called actionPerformed
    • If the listener needs private variables of the main component, it is best to define that listener as an inner class within the main component class, as inner classes can access private members
    • Another approach is that the main component itself implementing the ActionListener class to provide the actionPerformed method, but it defies the object-oriented programming principles. Your code in that actionPerformed method may also become too complex if there are many buttons on your screen making it difficult to read
  • You are designing a Composition relationship between classes. For example, a Hand class doesn’t have a meaning outside the Body class, unless you are writing a horror game where hands can crawl on their own 🙂
  • Basically any class which is in HAS-A relation needs private members of the HAVING class, it can be declared as an inner class within the HAVING class itself

There are 2 more variants of Inner Classes, those are Local Classes and Anonymous Classes.

Local Inner Classes

  • Classes defined inside a block of code are called local inner classes.
  • That block of code can be either a method, control structures like if, while, etc., or just an empty block within a method.
package com.techstackjournal.nested.ex2;

public class OuterClass {

	private String privateVariable = "privateVariable of outer class";
	String defaultVariable = "defaultVariable";
	public String publicVariable = "publicVariable";
	protected String protectedVariable = "protectedVariable";

	public void display(String methodParameter) {

		String localVariable = "localVariable";

		class InnerClass {
			private String privateVariable = "privateVariable of inner class";

			private static final String CONSTANT_VARIABLE = "CONSTANT VARIABLE";

			public InnerClass() {
				System.out.println("inside non-static method " + privateVariable);
				System.out.println("inside non-static method " + OuterClass.this.privateVariable);
				System.out.println("inside non-static method " + defaultVariable);
				System.out.println("inside non-static method " + publicVariable);
				System.out.println("inside non-static method " + protectedVariable);
				System.out.println("inside non-static method " + CONSTANT_VARIABLE);
				System.out.println("inside non-static method " + methodParameter);
				System.out.println("inside non-static method " + localVariable);
			}
		}

		new InnerClass();

	}

	public static void displayStatic(String methodParameter) {

		String localVariable = "localVariable";

		class InnerClass {
			private String privateVariable = "privateVariable of inner class";

			private static final String CONSTANT_VARIABLE = "CONSTANT VARIABLE";

			public InnerClass() {
				System.out.println("inside static method " + this.privateVariable);
				// System.out.println(OuterClass.this.privateVariable);
				// System.out.println(defaultVariable);
				// System.out.println(publicVariable);
				// System.out.println(protectedVariable);
				System.out.println("inside static method " + CONSTANT_VARIABLE);
				System.out.println("inside static method " + methodParameter);
				System.out.println("inside static method " + localVariable);
			}
		}

		new InnerClass();

	}
}
package com.techstackjournal.nested.ex2;

public class Application {

	public static void main(String[] args) {
		new OuterClass().display("methodParameter");
		OuterClass.displayStatic("methodParameter");
	}

}
inside non-static method privateVariable of inner class
inside non-static method privateVariable of outer class
inside non-static method defaultVariable
inside non-static method publicVariable
inside non-static method protectedVariable
inside non-static method CONSTANT VARIABLE
inside non-static method methodParameter
inside non-static method localVariable
inside static method privateVariable of inner class
inside static method CONSTANT VARIABLE
inside static method methodParameter
inside static method localVariable
  • A local class cannot be defined with access modifiers. Only abstract and final modifiers are permitted.
  • Similar to inner classes, local classes also cannot have any static members unless the variable is a constant (defined as final)
  • You can create an instance of inner class just like any other class but only within the method in which it is declared
  • An instance of the local inner class can be created only below to the class declaration
  • A local inner class can access enclosing method parameters and local variables but it cannot modify them. Those variables are effectively final for local inner class though they are not declared final.
  • If a local inner class is defined within a static method, it can only access the static variables of the enclosing class. It cannot access members of the enclosing class because the enclosing method will not be associated with any object
  • A local class cannot be a static class, even if it is present within a static method
  • You cannot declare an interface within a method. An interface can only be defined inside a top-level class or interface or in a static context. Here static context means a nested static class.

Anonymous Inner Classes

  • Anonymous Inner Classes are similar local inner classes but without a name
  • Since anonymous inner classes don’t have a name, they are to be instantiated at the time of definition itself
  • Even though anonymous inner classes don’t have a name, they have to be in IS-A relation, so that we can create an instance of it. An anonymous inner class will either be defined by implementing an interface or by inheriting a superclass, but without writing implements or extends explicitly
SuperType st = new SuperType() {
    // anonymous inner class definition
};
  • In the above code, SuperType is either an existing class or interface. We are actually not creating the instance of SuperType here, instead, we are defining and creating an instance of an anonymous class which is either extending or implementing from SuperType
  • Make sure that the definition of the anonymous inner class ends with a semicolon, as mentioned in the 2nd line of the above code snippet
  • Methods defined within the implemented interface or superclass are only accessible using the reference variable (remember Polymorphism)
  • New methods defined within the anonymous inner class which doesn’t exist in a superclass or interface are accessible outside the class only right after the class definition
new SuperType() {
    public void newMethod() {
        System.out.println("new method");
    }
}.newMethod();
  • An Anonymous Inner class can implement only one interface at a time
  • It cannot implement an interface and extend a class at the same time
  • Anonymous inner classes make your local inner classes look more concise

Creating Anonymous Inner Class from a Class

Let’s see an example of an anonymous inner class defined using an existing class.

package com.techstackjournal.nested.ex4;

public abstract class Alpha {
	abstract void alphaMethod();
}

Alpha is our existing abstract class with one abstract method.

package com.techstackjournal.nested.ex4;

public class OuterClass {
	
	public void display() {
		Alpha alpha = new Alpha() {
			@Override
			void alphaMethod() {
				System.out.println("In Alpha subclass");
			}
		};
		alpha.alphaMethod();
	}

}

In the above class, within the display method, we are defining an anonymous class using the Alpha class. We are not creating an instance of Alpha (anyway it is not possible as it is an abstract class), instead, we are defining and creating an instance of an anonymous class which is of type Alpha. You can see that we’ve not written extends keyword anywhere, but Java implicitly considers this syntax as extending from Alpha class.

package com.techstackjournal.nested.ex4;

public class Application {
	public static void main(String[] args) {
		new OuterClass().display();
	}
}
In Alpha subclass

Creating Anonymous Inner Class from Interface

In this example, we will define an anonymous inner class using an existing interface.

package com.techstackjournal.nested.ex4;

public interface Beta {
	void betaMethod();
}
package com.techstackjournal.nested.ex4;

public class OuterClass {
	
	public void display() {
		Beta beta = new Beta() {
			@Override
			public void betaMethod() {
				System.out.println("In Beta implementer ");
			}
		};
		
		beta.betaMethod();
	}
}

In the above class, within the display method, we are defining an anonymous class implementing the Beta interface. We are not creating an instance of Beta (anyway creating an instance of the interface is not possible), instead, we are defining and creating an instance of an anonymous class which is of type Beta. We’ve not written any implements keyword here, but implicitly Java will consider this syntax as interface implementation.

package com.techstackjournal.nested.ex4;

public class Application {
	public static void main(String[] args) {
		new OuterClass().display();
	}
}
In Beta implementer

Passing an Anonymous Inner Class instance as Method Argument

What we are looking in this example, is just another style of writing the code. Instead of storing the instance of an anonymous class in a reference variable, we’ll pass its instance directly as a method argument.

package com.techstackjournal.nested.ex4;

public interface Beta {
	void betaMethod();
}
package com.techstackjournal.nested.ex4;

public class OuterClass {
	
	public void display() {
		displayBeta(new Beta() {
			@Override
			public void betaMethod() {
				System.out.println("In Beta implementer as parameter");
			}
		});

	}
	
	private void displayBeta(Beta beta){
		beta.betaMethod();
	}

}

In the above example, while calling the displayBeta method, within its arguments section, we are defining and instantiating an anonymous inner class implementing from Beta interface.

package com.techstackjournal.nested.ex4;

public class Application {
	public static void main(String[] args) {
		new OuterClass().display();
	}
}
In Beta implementer as parameter

Static Nested Class

  • A class defined within another class as static is a static nested class
  • A static nested class is just like any other top-level classes in all aspects, only thing is it is written in another top-level class for logical grouping
  • As a class can access its static variables and static methods without creating an instance, it can also access static classes without any instance of its own
  • A static nested class can have it’s own objects independently, they are not tied with the outer class objects.
  • Like within inner classes, static nested classes can be instantiated normally just like any other class within the outer class
    NestedClass nested = new NestedClass();
  • If we need to create an instance outside of the outer class, you just need to use the outer class name followed by nested class (OuterClass.NestedClas), you don’t need to instantiate outer class before that.
    OuterClass.NestedClass nested = new OuterClass.NestedClass();
  • A static nested class can only access static variables of the outer class, it cannot access the instance variables just like static methods of a top-level class

Example of Static Nested Class

package com.techstackjournal.nested.ex5;

public class OuterClass {
	public static String staticVariable = "static variable";
	public NestedClass nested = new NestedClass();
	
	public static class NestedClass {
		public void display() {
			System.out.println("Inside nested class member method");
			System.out.println(staticVariable);
		}
	}

}
package com.techstackjournal.nested.ex5;

public class Application {

	public static void main(String[] args) {

		OuterClass.NestedClass nested = new OuterClass.NestedClass();
		nested.display();

		OuterClass outer = new OuterClass();
		outer.nested.display();

	}

}
Inside nested class member method
static variable
Inside nested class member method
static variable