Inner Classes
From CS315
A nested class is a class declared within the body of another class or interface. There are two types of nested classes: static nested classes and non-static nested classes. Non-static nested classes are called inner classes. While there is only one kind of static nested class there are several types of inner classes: member inner classes, local inner classes, anonymous inner classes.
Contents |
Summary
| Access Modifers | Access Outer Class Static Members | Have Static Members | Access Outer Class Non-Static Members | Have Non-Static Members | Create Instance Independent of Outer Class | ||
| Nested Classes | Static Inner Classes | private public protected default(package) | Yes | Yes | No | Yes | Yes |
| Non-static Inner Classes | Yes | No | Yes | Yes | No | ||
Rules & Exceptions
Rule: Inner class objects cannot exist without being attached to an outer class object
Exception: If the inner class is declared in a static method it assumes the behavior of a nested class
Rule: If an inner class is declared public an instance can be created using:
Outer.Inner var = outerobject.new Inner();
Exception: Although legal, this is generally bad design, if the inner class is not private there is little need to use an inner class to begin with
Rule: Outer.this.innerMember can be shortened to innerMember to access inner class members from within the inner class. The inner class can also access the outer class members this way. Outer.outerMember can be shortened to outerMember
Exception: Accessing members by name only will shadow the enclosing class' members if they have the same name as the inner class members. An outer class member can always be accessed using Outer.memberName
Why Nest Classes
Encapsulation
Encapsulation is increased by using inner classes. Inner classes help you hide the implementation of a class completely.
Organization
Nested classes provide a way to logically group classes that are only used in one place. If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
Readable Code
Nesting small classes within top-level classes places the code closer to where it is used.
Nested Classes
There are two types of nested classes: static and non-static.
Static
A static nested class is essentially a top level class that has been nested inside another top level class to increase encapsulation. A static nested class cannot refer directly to instance variables or methods defined in its enclosing class, it must use them through an object reference. A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class.
Static nested classes can be accessed through the outer class name: OuterClass.StaticNestedClass
To create an object for the static nested class we use the sytax: OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Non-Static
Non-static nested classes are called inner classes. There are three types: Member, Local, Anonymous.
Member Class
A member inner class is a class whose definition is directly enclosed by another class or interface declaration. An instance of a member inner class can only be created if you have a reference to an instance of its outer class. A member class can access all of the enclosing classes members through an enclosing class object reference. As usual, from within an inner class, you can use the keyword this to reference the current instance (the inner class's instance). To reference the enclosing class's instance you use this syntax: EnclosingClassName.this
class TopLevel {
private int value = 9;
class Inner {
int value = 18;
int calculate () {
return value;
}
}
}
public class MemberInnerTest {
public static void main (String[] args) {
TopLevel toplevel = new TopLevel ();
TopLevel.Inner inner = new TopLevel.Inner();
System.out.println(toplevel.value); //doesn't compile, value is private in TopLevel class
System.out.println(inner.calculate()); //prints 18
}
}
Local Class
What distinguishes a local class from a member class is that a local class is not declared directly inside an enclosing class, it is declared within a block of code inside the enclosing class. A local class can be declared inside any block of code, and its scope is within the block. For example, you can declare a local class within a method, an if block, a while block, and so on. Local classes have a name, as opposed to anonymous classes that do not.
Being enclosed inside a block of code means that local classes can access the local variables inside a method, if, while, etc. However, in order to do have access to local variables they must be declared final or a compiler error will occur.
interface LocalInnerInterface {
int calculate();
}
class TopLevel {
private int value = 9;
public LocalInnerInterface local(){
final int value = 18;
class LocalInner implements LocalInnerInterface{ //Local inner class cannot be static
private int value1 = 10;
// private static int value2 = 10; //Cannot declare a static type in an inner class
private static final int value3 = 5; //Unless it is declared final
public int calculate() { //must be public because visibility
//from interface cannot be reduced
return value + TopLevel.this.value + this.value3 + value1; // 18 + 9 + 5 + 10
}
}
return new LocalInner(); //Have to define interface and implement with inner class
//otherwise we can't return the inner class type
}
}
final class LocalInnerTest {
public static void main (String[] args) {
TopLevel toplevel = new TopLevel ();
System.out.println(toplevel.local().calculate()); //prints 42
}
}
Anonymous Class
A local class that does not have a name is called an anonymous class. A use of this type of nested class is for writing an interface implementation. As in the following simple example:
interface Printable { //can also be an abstract class
void print (String message);
}
public class AnonymousInnerClassTest{
public static void main (String [] args) {
Printable printer = new Printable() {
public void print (String message) {
System.out.println (message);
}
}; // this semicolon is required to terminate the statement that instantiates the anonymous inner class.
printer.print ("Beach Music");
}
}
<br>
The interesting thing here is that you create an anonymous inner class by using the new keyword followed by what looks like a class's constructor (in this case Printable()). However, note that Printable is an interface and does not have a constructor. This seemingly illegal call is handled gracefully when the code is compiled. The java complier will rewrite the code, creating a name for the anonymous class as well as a added a constructor to the class and will be calling that constructor. See section “Compiler & JVM”. Also, note that after the closing brace, we use a semicolon to terminate the statement that instantiates the anonymous inner class.
Compiler & JVM
For all of you compiler gurus out there this is the machinery that makes the inner class work.
The JVM does not know the notion of nested classes. It is the compiler that works hard to compile an inner class into a top level class incorporating the outer class name and the inner class name as the name, both separated by a dollar sign. That is, the code that employs an inner class called Inner that resides inside Outer like this:
public class Outer {
class Inner {
}
}
will be compiled into two classes: Outer.class and Outer$Inner.class.
For anonymous classes, the compiler takes the liberty of generating a name for them, using numbers, i.e. Outer$1.class, Outer$2.class, etc.
When a nested class is instantiated, the instance lives as a separate object in the heap. It does not actually live inside the outer class object. However, with inner class objects, they have an automatic reference to the outer class object as shown. This reference does not exist in an instance of static nested class, because a static nested class does not have access to its outer class's instance members. This happens because the compiler changes the constructor of the inner class when the inner class is compiled, it adds an argument to every constructor. This argument is of type the outer class.
For example:
public Inner ()
is changed to this.
public Inner(Outer outer)
And, this
public Inner(int value)
to
public Inner (Outer outer, int value)
The compiler has the discretion to change the code it compiles. If a class (top level or nested) does not have a constructor, it adds a no-arg constructor to it. The code that instantiates an inner class is also modified, with the compiler passing a reference to the outer class object to the inner class constructor. If you code:
Outer outer = new Outer (); Outer.Inner inner = outer.new Inner ();
the compiler changes it to
Outer outer = new Outer (); Outer.Inner inner = outer.new Inner (outer);
When an inner class is instantiated inside the outer class, of course, the compiler passes the current instance of the outer class object using the keyword this.
// inside the Outer class Inner inner = new Inner ();
becomes
// inside the Outer class Inner inner = new Inner (this);
Now, here is another piece of the puzzle. How does a nested class access its outer class's private members? No object is allowed to access another object's private members. Again, the compiler changes your code, creating a method that accesses the private member in the outer class definition. Therefore,
class TopLevel {
private int value = 9;
class Inner {
int calculate () {
return value;
}
}
}
is changed to two classes like this:
class TopLevel {
private int value = 9;
TopLevel () {
}
// added by the compiler
static int access$0 (TopLevel toplevel) {
return toplevel.value;
}
}
class TopLevel$Inner {
final TopLevel this$0;
TopLevel$Inner (TopLevel toplevel) {
super ();
this$0 = toplevel;
}
int calculate () {
// modified by the compiler
return TopLevel.access$0 (this$0);
}
}
The addition happens in the background so you will not see it in your code. The compiler adds the access$0 method that returns the private member value so that the inner class can access the private member.
Written by Joe Elizondo
