Tech and Media Labs
This site uses cookies to improve the user experience.




Java Nested Classes

Jakob Jenkov
Last update: 2015-03-09

In Java nested classes are classes that are defined inside another class.

The purpose of a nested class is to clearly group the nested class with its surrounding class, signaling that these two classes are to be used together. Or perhaps that the nested class is only to be used from inside its enclosing (owning) class.

Java developers often refer to nested classes as inner classes, but inner classes (non-static nested classes) are only one out of several different types of nested classes in Java.

In Java nested classes are considered members of their enclosing class. Thus, a nested class can be declared public, package (no access modifier), protected and private (see access modifiers for more info). Therefore nested classes in Java can also be inherited by subclasses as explained in my tutorial about Java inheritance.

You can create several different types of nested classes in Java. The different Java nested class types are:

  • Static nested classes
  • Non-static nested classes
  • Local classes
  • Anonymous classes

All these types of nested classes will be covered in the following sections.

Static Nested Classes

Static nested classes are declared in Java like this:

public class Outer {

  public static class Nested {

  }

}

In order to create an instance of the Nested class you must reference it by prefixing it with the Outer class name, like this:

Outer.Nested instance = new Outer.Nested();

In Java a static nested class is essentially a normal class that has just been nested inside another class. Being static, a static nested class can only access instance variables of the enclosing class via a reference to an instance of the enclosing class.

Non-static Nested Classes (Inner Classes)

Non-static nested classes in Java are also called inner classes. Inner classes are associated with an instance of the enclosing class. Thus, you must first create an instance of the enclosing class to create an instance of an inner class. Here is an example inner class definition:

public class Outer {

  public class Inner {
  }

}

Here is how you create an instance of the Inner class:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

Notice how you put new after the reference to the outer class in order to create an instance of the inner class.

Non-static nested classes (inner classes) have access to the fields of the enclosing class, even if they are declared private. Here is an example of that:

public class Outer {

    private String text = "I am private!";

    public class Inner {

        public void printText() {
            System.out.println(text);
        }
    }
}

Notice how the printText() method of the Inner class references the private text field of the Outer class. This is perfectly possible. Here is how you would call the printText() method:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.printText();

Inner Class Shadowing

If a Java inner class declares fields or methods with the same names as field or methods in its enclosing class, the inner fields or methods are said to shadow over the outer fields or methods. Here is an example:

public class Outer {

    private String text = "I am Outer private!";

    public class Inner {

        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text);
        }
    }
}

In the above example both the Outer and Inner class contains a field named text. When the Inner class refers to text it refers to its own field. When Outer refers to text it also refers to its own field.

Java makes it possible though, for the Inner class to refer to the text field of the Outer class. To do so it has to prefix the text field reference with Outer.this. (the outer class name + .this. + field name) like this:

public class Outer {

    private String text = "I am Outer private!";

    public class Inner {

        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text);
            System.out.println(Outer.this.text);
        }
    }
}

Now the Inner.printText() method will print both the Inner.text and Outer.text fields.

Local Classes

Local classes in Java are like inner classes (non-static nested classes) that are defined inside a method or scope block ({ ... }) inside a method. Here is an example:

class Outer {

    public void printText() {

        class Local {

        }

        Local local = new Local();
    }

}

Local classes can only be accessed from inside the method or scope block in which they are defined.

Local classes can access members (fields and methods) of its enclosing class just like regular inner classes.

Local classes can also access local variables inside the same method or scope block, provided these variables are declared final.

From Java 8 local classes can also access local variables and parameters of the method the local class is declared in. The parameter will have to be declared final or be effectually final. Effectually final means that the variable is never changed after it is initialized. Method parameters are often effectually final.

Local classes can also be declared inside static methods. In that case the local class only has access to the static parts of the enclosing class. Local classes cannot contain all kinds of static declarations (constants are allowed - variables declared static final), because local classes are non-static in nature - even if declared inside a static method.

The same shadowing rules apply for local classes as for inner classes.

Anonymous Classes

Anonymous classes in Java are nested classes without a class name. They are typically declared as either subclasses of an existing class, or as implementations of some interface.

Anonymous classes are defined when they are instantiated. Here is an example that declares an anonymous subclass of a superclass called SuperClass:

public class SuperClass {

  public void doIt() {
    System.out.println("SuperClass doIt()");
  }

}

SuperClass instance = new SuperClass() {

    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt();

Running this Java code would result in Anonymous class doIt() being printed to System.out. The anonymous class subclasses (extends) SuperClass and overrides the doIt() method.

A Java anonymous class can also implement an interface instead of extending a class. Here an example:

public interface MyInterface {

  public void doIt();

}
MyInterface instance = new MyInterface() {

    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt();

As you can see, an anonymous class implementing an interface is pretty similar to an anonymous class extending another class.

An anonymous class can access members of the enclosing class. It can also access local variables which are declared final or effectively final (since Java 8).

You can declare fields and methods inside an anonymous class, but you cannot declare a constructor. You can declare a static initializer for the anonymous class instead, though. Here is an example:

final Strint textToPrint = "Text...";

MyInterface instance = new MyInterface() {

    private String text;

    //static initializer
    {  this.text = textToPrint;  }

    public void doIt() {
        System.out.println(this.text);
    }
};

instance.doIt();

The same shadowing rules apply to anonymous classes as to inner classes.

Nested Class Benefits

The benefits of Java nested classes are that you can group classes together that belong together. You could do so already by putting them in the same package, but putting one class inside another makes an even stronger grouping.

A nested class is typically only used by or with its enclosing class. Sometimes a nested class is only visible to the enclosing class, is only used internally, and is thus never visible outside the enclosing class. Other times the nested class is visible outside its enclosing class, but can only be used in conjunction with the enclosing class.

An example would be a Cache class. Inside the Cache class you might declare a CacheEntry class which can contain information about a specific cache entry (cached value, time inserted, number of times accessed etc.). Users of the Cache class may never see the CacheEntry class, if they have no need to obtain information about the CacheEntry itself, but only the cached value. However, the Cache class may choose to make the CacheEntry class visible to the outside world, so they can access more than just the cached value (for instance information about when the value was last refreshed etc.).

Here are two Cache implementation skeletons illustrating the points:

public class Cache {

    private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();

    private class CacheEntry {
        public long   timeInserted = 0;
        public object value        = null;
    }

    public void store(String key, Object value){
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if(entry == null) return null;
        return entry.value;
    }

}
public class Cache {

    private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();

    public class CacheEntry {
        public long   timeInserted = 0;
        public object value        = null;
    }

    public void store(String key, Object value){
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if(entry == null) return null;
        return entry.value;
    }

    public CacheEntry getCacheEntry(String key) {
        return this.cacheMap.get(key);
        }

}

The first Cache class hides its CacheEntry nested class while the second Cache class exposes it.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC