Java Nested Classes Nested classes in Java are classes that are defined inside another class. The purpose of a nested...

Java Nested Classes

09:33 PASSOVE INCOME 1 Comments

                 Java Nested Classes

Nested classes in Java 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.
Nested classes are considered members of their enclosing class. Thus, a nested class can be declaredpublicpackage (no access modifier), protected and private (see access modifiers for more info).

Static Nested Classes

Static nested classes are declared like this:
public class Outer {

  public static class Nested {

  }

}
In order to create an instance of Nested you must reference it by prefixing it with the Outer class name, like this:
Outer.Nested instance = new Outer.Nested();
A static nested class is essentially a normal class that has just been nested inside another class. It interacts with its enclosing class in the same way. 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 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.
Non-static nested 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 theOuter 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();

Shadowing

If an 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 Innerclass refers to text it refers to its own field. When Outer refers to text it also refers to its own field.
It is possible for the Inner class to refer to the text field of the outer class. To do so it has to prefix thetext field reference with Outer.this. 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 are like inner (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 they 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 are classed 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 code would result in Anonymous class doIt() being printed to System.out. The anonymous class subclasses (extends) SuperClass and overrides the doIt() method.
Anonymous classes 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, this 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.
You can declare fields and methods inside an anonymous class, but you cannot declare a constructor. You can declare a static initializer 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 for inner classes.

Nested Class Benefits

The benefits of nested classes is that you can group classes together that belong together. You could do so alreay 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 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.
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 class while the second Cache class exposes it.

1 comment: