ZOFTINO.COM android and web dev tutorials

Java Nested Classes Tutorial

Like fields and methods, you can define a class inside a class. The classes which are declared inside a class are called nested classes. Similar to the access modifiers used for controlling access to member fields and methods of a class, inner classes can also be declared with access modifiers.

This tutorial helps you in learning all the concepts of nested classes with examples. Before you read further, see Java programming basics and Java classes and objects if you need to learn java basics.

Table of Contents

Nested Classes

In Java, you can define a class inside another class. For example, Page class is declared as a nested class of Book class.

 public class Book {
	private String title;
	public void setPages(int pages) {
		this.pages = pages;
	}
	class Page {
		private int pageNo;		
	}
} 

Classes can be declared as public or default no modifier (package private), but nested class can be defined as private, public, protected and no modifier (package private).

Nested classes can be classified into two types depending on whether they can access members of the enclosing class or not. Two types of nested classes are static nested classes and non static nested classes.

Static Nested Classes

Like member fields and methods, nested classes can also be defined as static. The nested classes which are declared as static are called static nested classes. Static nested classes can’t access members of the enclosing class.

Like static fields and methods, static nested classes are also class level and not associated with the objects of enclosing class. That is why static nested classes can’t access member fields and methods of outer class directly.

 public class Store {
	private String name;
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}

	static class Coupon{
		private String offer;
		private int dicount;
		public String getOffer() {
			return offer;
		}
		public void setOffer(String offer) {
			this.offer = offer;
		}
		public int getDicount() {
			return dicount;
		}
		public void setDicount(int dicount) {
			this.dicount = dicount;
		}	
	}

}
 

You can instantiate an object for the static nested classes, like any other top level classes. But to access the nested static classes, you need to use outer class name as shown below.

 	//outer class instance
		Store s = new Store();
		s.setName("amazon");
		
		//static inner class instance
		Store.Coupon storeCoupon = new Store.Coupon();
		storeCoupon.setDicount(12);
		storeCoupon.setOffer("flat 10% off on mobiles");

Non Static Nested Classes or Inner Classes

Classes which are declared inside another class without static are called non static nested classes or inner classes. Similar to non static member fields and methods of a class, inner classes are associated with the instance of the outer class. Meaning to access the inner class, first you need to create an instance of the outer class.

Like member fields and methods of the class, inner classes can access member variables and methods of enclosing class.

public class Book {
	private String title;
	private int pages;
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public int getPages() {
		return pages;
	}
	public void setPages(int pages) {
		this.pages = pages;
	}

	class Page {
		private int pageNo;
		private String content;
		public int getPageNo() {
			return pageNo;
		}
		public void setPageNo(int pageNo) {
			this.pageNo = pageNo;
		}
		public String getContent() {
			return content;
		}
		public void setContent(String content) {
			this.content = content;
		}
		public String getBookPageInfo() {
			return title + " "+pageNo;
		}
		
	}

} 

To instantiate Page object, first instantiate Book object. Then using the Book object, Page object can be instantiated.

 	//instance of outer class
		Book b = new Book();
		b.setTitle("Java Development");
		b.setPages(400);
		
		//instance of inner class using outer class
		Book.Page bp =  b.new Page();
		bp.setPageNo(45);
		System.out.println("Book page info : "+bp.getBookPageInfo()); 

The example also shows that inner class can directly access the member variables of outer class. The method getBookPageInfo() of inner class directly accesses title variable of outer class.

Shadowing

Like the this keyword is used to refer to a member variable name in the constructors or methods when the name of one of the parameters of them is same as the name of member variable, there is a way to refer to inner class member variable with the same name as the name of one of the parameters of the inner class methods or constructors and name of the member variable of outer class.

For example, Cart class has amount variable, Tax, inner class of Cart, has amount variable and amount is a parameter name of calculateTax() method of Tax. The example shows how to access all three variable of the same name in the calculateTax () method.

public class Cart {
	private int amount;

	public int getAmount() {
		return amount;
	}

	public void setAmount(int amount) {
		this.amount = amount;
	}
	
	class Tax{
		private int amount;

		public int getAmount() {
			return amount;
		}

		public void setAmount(int amount) {
			this.amount = amount;
		}
		
		public int calculateTax(int amount) {
			System.out.println("calculateTax argument amount "+amount);
			System.out.println("Tax object amount "+this.amount);
			System.out.println("Cart object amount "+Cart.this.amount);
			
			return (Cart.this.amount - (amount+this.amount/2))*10/100;
		}
	}
}
 
	Cart cart = new Cart();
		cart.setAmount(600);
		
		Cart.Tax tax = cart.new Tax();
		tax.setAmount(200);
		
		tax.calculateTax(100); 

Output:

calculateTax argument amount 100
Tax object amount 200
Cart object amount 600

Local Classes

Inner classes which are defined in a blocks such as if statement, while loop or methods are called local classes. Like inner classes, local classes can also access member fields and methods of outer class. For example, Discount class is defined as local inner class of CartItem. The example shows accessing member variable of outer class price in local class.

 public class CartItem {
	private float price;
	
	public float getPrice() {
		return price;
	}

	public void setPrice(float price) {
		this.price = price;
	}

	public float getMaxDisocunt(String discOne, String discTwo) {
		final String status;
		//local class
		class Discount{			
			public float getDiscount(String discType) {
				
				//local class accessing member variable of outer class
				if(price > 1000 && "cashback".equals(discType)) {
					return price*10/100;
				}if(price < 1000 && "cashback".equals(discType)) {
					return price*18/100;
				}else {
					return price*15/100;
				}
			}
			//local class can access parameters of the method in which it is declared
			public void describeComparision() {
				System.out.println("discount check of : "+discOne + " & "+discTwo);
			}
		}
		//instantiating local class
		Discount discount = new Discount();
		
		discount.describeComparision(); 
		
		float cbdisc = discount.getDiscount(discOne);
		System.out.println("discount cashback : "+cbdisc);
		float cdisc = discount.getDiscount(discTwo);
		System.out.println("discount coupon : "+cdisc);
		
		if(cbdisc > cdisc) {
			return cbdisc;
		}else {
			return cdisc;
		}
	}
}
	CartItem ci =  new CartItem();
		ci.setPrice(800);
		ci.getMaxDisocunt("cashback", "coupon");	 

Output:

discount check of : cashback & cashback
discount cashback : 144.0
discount coupon : 120.0 

Local class can access local variables defined within the block in which the inner class is declared. But the local variable has to be final or effective final. Similarly, local class can access parameters of method in which it is declared. Again method parameters have to be final or effective final.

Starting Java SE 8, local variable or method parameter can be accessed from local class as long as values of them are not changed. The variables, which are never changed after initialization, are called effective final variables.

In the example shown above, method parameters discOne and discTwo (effective final) are accessed from local class method describeComparision().

If local class is part of a static method, then it can access only static members of outer class. In the example below, Vendor local class is part of a static method. So it can access only static members of outer class like static variable level in the example, but it can’t access non static member like variable type in the example.

public class Reservation {
	private static int level;
	private String type;
	
	public static void setLevel(int rlevel) {		
		level = rlevel;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public static int getAvaiablity(int lvl1, int lvl2){		
		
		class Vendor{
			public int getSeat(int lvl) {
				if(level == lvl) {
					return 1;
				}else {
					return 0;
				}

			}
		}
		
		Vendor vendor = new Vendor();
		int seat1 = vendor.getSeat(lvl1);
		if(seat1 != 0) {
			return seat1;
		}
		return vendor.getSeat(lvl2);
	}
}

Local classes like inner classes can’t define static members, as local classes are associated with instances of outer class. But local class can declare constants using static and final.

 public static void testOne() {
	
	class Result{
		public static final String TOP_RESULT = "80";
        public void getTopResult() {
            System.out.println(TOP_RESULT);
        }
		
	}
	Result r = new Result();
	r.getTopResult();
}

Anonymous Classes

Similar to local classes anonymous classes are defined in a block but without a class name and used only once. Anonymous classes are declared and instantiated at the same time.

For example, you have one interface and one of its implementation is used only inside a class and it is used once. You can implement the interface as anonymous class inside a method of the outer class.

Anonymous class expression contains new operator, interface name, parenthesis with or without arguments and body with method declarations.

In the example below, AddListener is an interface, which is implemented in the constructor of AudioPlayer class as anonymous class.

public interface AddListener {
	public void onAddition(String s);
}
 public class AudioPlayer {
	
	private String currentSong;
	private AddListener listener;

	public AudioPlayer() {
		//AddListener interface is implemented as anonymous class
		listener = new AddListener() {			
			@Override
			public void onAddition(String s) {
				System.out.println("current song "+s);
				AudioPlayer.this.playSong();
			}
			
		};
	}
	public String getCurrentSong() {
		return currentSong;
	}

	public void setCurrentSong(String currentSong) {
		this.currentSong = currentSong;
		//anonymous class instance is used
		listener.onAddition(currentSong);
	}
	public void playSong() {
		System.out.println("playing song "+currentSong);
	}
	
}
 		AudioPlayer audioPlayer = new AudioPlayer();
		audioPlayer.setCurrentSong("hello hello...");

Output:

current song hello hello...
playing song hello hello...

Like local class, anonymous classes can access members of outer class and local variables which are final or effective final. Constructors can’t be declared in anonymous classes.

Nested Classes Access Modifiers

Similar to member fields and methods, nested classes can be public, private, package private and protected, see class and members access modifiers for more information.

Both static and non static nested classes can instantiated everywhere if they are declared as public. They can be instantiated only in the classes which belong to the same package in which the nested classes are created if the nested classes are declared without any modifier. Protected nested classes can be accessible from the classes in the same package and from the sub classes from any package. Private nested classes can be accessible within the enclosing class.

For example, protected nested class can be instantiated in sub class which exists in different package.

 public class Cart {	
   protected class Tax{

	}
} 

MainCart which is a subclass of Cart and in different package can instantiate protected inner class Tax declared in Cart.

 public class MainCart extends Cart{
	public static void getCartTax(String[] args) {
		MainCart m = new MainCart();
		Cart.Tax t = m.new Tax();
	}
} 

Uses of Nested Class

If a class can be used only inside another class, then defining the class as nested class makes logical sense and streamlines the packaging.

Defining nested classes increases the encapsulation as the members of the class and nested classes are hidden to outside entities.

Code maintenance becomes easy as related code exist together.