| RHS => Allan => Prog2 => Notes => UsingNestedClasses |
Updated:
02/02/2004 23:36 WebMaster: allanhn@rhs.dk |
A nested class is a class that's defined (declared) inside another class.
Often nested classes are called inner classes and we'll use this term
(even if it's not completely accurate).
Throughout this note we'll use the term "outer class" for
the enclosing class and "inner class" for the class declared
inside the outer class.
| Inner classes are used to |
|
Member classes can be static or non-static.
Static member class (a.k.a
Nested top-level class)
Static member classes allows simple structuring of types (classes).
Normally used for classes that serves the outer class or defines components
of the outer class (e.g. Entry in the interface Map (from
the collections framework) or a class Wheel inside
a class Car).
All instantiations from outer class uses same declaration of inner class (it's static).
The inner class has same top-level status as outer class (as if it was declared in a file of it's own), it can extend / implement another class / interface and can be sub-classed (extended). It's name and accessibility is defined by the outer class, name: OuterClassName.InnerClassName, accessible: only via outer class.
An inner object is a member of the outer object so it has access to other members of outer objects (incl. private members).
As an ordinary member the inner class can have ordinary modifiers (final, private, public etc.).
Non-static member class
Like any ordinary member. Normally used as helper class (e.g. Iterator or
adapter) for outer class.
Inner class has access to members of outer class (including private members),
the outer class has access to members of inner class through a reference to
the inner object. Inner class is not allowed to have any static members (except
for final constants).
Inner instance (object) is associated with outer instance (object) when inner
instance is created (normally done inside method of outer class) - this means
that the inner object always holds a reference to outer object and the inner
object can only be created via an outer object.
This association can't be modified! This means that if we from outside of the
outer object keep a reference to the inner object we don't loose the outer object
even if we change our reference to it. Via the reference to the inner object
there is a association (reference) to the outer object so it still exists!
Note that if inner class is declared public it it possible to instantiate
an inner object from outside of the outer object (but remember that we must
have an outer object). Often this isn't what you want!
Note that from inside of the non-static inner class this refers to the
inner object, OuterClassName.this refers to the outer object.
Similar
rules for new and super.
Rules for accessibility, extending / implementing classes / interfaces follow
rules for static member classes.
If association to outer object is not required (inner object doesn't need to access anything from outer object) the inner class should be declared static.
If outer class is public it's very important to make the right decision whether inner class should be static or not. Remember that non-static inner classes always hold the association to outer object.
Local class
Named member of method or block, defined in an expression.
Not member of outer object but local to method / block.
Objects are inaccessible outside method / block but are normal objects that can be passed as parameters or returned from methods. Only permitted modifier is final.
Can access everything that is in scope when class is defined. Local variables must be final because the inner object may survive the method that created it (e.g. reference to inner object is returned to outside variable). When the method finishes the local variables no longer exists, but the inner object must still "see" the values as they were when the object was instantiated - thus they must be frozen (be final) in order to reflect the situation as it was then. If inner class must refer to non-final variables you can copy their values to final variables which are then used by inner class.
Normally used as an adapter class.
Not used very often as the three other alternatives covers most situations.
Anonymous inner class
Unnamed member of a method or block, extends a class or implements an interface.
Defined in the new expression as part of a statement.
The type specified to new is the supertype of the
anonymous class - new invokes
supertype constructor (as usual when instantiation objects). It cannot explicit extend
or implement class / interface but will implicit extend or implement
given supertype.
Typically only implements / overrides methods from interface or abstract superclass
- no new methods as they can't be accessed from outside.
Defined and instantiated only once where needed. Used only if it is to be instantiated
at a single point.
Behaves like non-static member classes if declared in non-static context. If
declared in static context behaves like static member classes.
No name or constructor, not a member of outer class (but local to method or block). It's not possible to refer to instance.
Normally used:
Simple and direct but can be difficult to read (and understand) - should not be more than 5-10 lines.
Inheritance
Matters of overriding, overloading, inheritance, extending
/ implementing can be extremely complex - specially when extending classes
/ implementing interfaces
that themselves are inner classes. Make things as simple as possible. See "The
Java Programming Language Third Edition" by Arnold, Gosling & Holmes
for further information.
How to choose?
If inner class needs to be visible outside of method (or it's very long) use
a member class.
If each instance needs an association to (is tied to) outer instance use non-static
otherwise use static.
If instance needed only at one point and it's implementing / extending existing
interface / class use anonymous class
Else use local class.
Examples
Static member class
Problem: We need a method to return what a particular person can do with
a given bankaccount. Multiple actions are possible so the method must return
multiple permissions. We declare a class to hold the set of permissions:
public class BankAccount {
private long number; //account number
private long balance; //current balance
public static class Permissions {
public boolean canDeposit;
public Boolean canWithdraw;
}
public Permissions permissionsFor(Person who) {
Permissions perm = new Permissions();
perm.canDeposit = mayDeposit(who);
perm.canWithdraw = mayWithdraw(who);
return perm;
}
public Boolean mayDeposit(Person p) {
if (.... to be implemented ....) return true;
else return false;
}
//mayWithdraw(Person p) to be implemented
}
To keep things simple the class Permissions (full name is BankAccount.Permissions)
is very small and the definition of class Person is omitted).
As the Permissions class is related to the contract of the BankAccount class (it is how the BankAccount communicates a set of permissions to others) it is a good candidate to be a
nested class. As an object of type Permissions doesn't need association to outer object (of type BankAccount)
it is static. Note that the method permissionsFor can refer to Permissions directly as they both are members of BankAccount.
This also means that if we declare a method in Permissions that gets a BankAccount-object
as parameter it would be able to access that BankAccount-object's
private variables (numberand balance) directly.
Non-static member class
Problem: We need a method that lets you see the last action performed
(and the value for that action) on a bankaccount.
public class BankAccount {
private long number; //account number
private long balance; //current balance
private Action lastAct; //last action performed
public class Action {
private String act;
private long amount;
Action(String act, long amount) {
this.act = act;
this.amount = amount;
}
}
public void deposit(long amount) {
balance = balance + amount;
lastAct = new Action("Deposit", amount);
}
public void withdraw(long amount) {
balance = balance - amount;
lastAct = new Action("Withdraw", amount);
}
//much more to be implemented....
}
The class Action records a single action. It is not static which means that an instance exists relative to an instance of the outer class (BankAccount). The association between the two instances is created when the Action object is created (in the deposit and withdraw methods). This association is an advantage because the Action object holds values for a specific BankAccount object - an Action object with no associated BankAccount object would not make any sense.
Note that the inner class is declared public which it probably shouldn't be! If the inner class has accessible methods that modifies variables in the outer object we can create an inner object and change the outer object through it. Here is an example (the example from above with a few changes) that shows how this is possible (the example also shows how the outer object persist to exist when we keep a reference to the inner object): NonStaticMemberEx.java
Local Inner class
Problem: We need a method that returns an Iterator to
walk through an array of objects.
import java.util.*;
//Method walkThrough is part of a class-declaration:
public static Iterator walkThrough(final Object[] objs) {
class Iter implements Iterator {
private int pos = 0;
public Boolean hasNext() {
return (Pos < objs.length);
}
public Object next() throws NoSuchElementException {
if (Pos >= objs.length) throw NoSuchElementException;
else return objs[Pos++];
}
public void remove() {
//not allowed here, but it's part of interface Iterator
//so we must implement it. Throw an exception:
throw new UnsupportedOperationException();
}
}
return new Iter();
}
The Iter class is local to the method walkThrough - it is not a member of the enclosing class. Iter has access to all the final variables of the method walkThrough - in particular the parameter objs (explicit declared final).
Anonymous Inner class
Problem: We don't like the solution above because Iter is
lightweight and not needed outside the method walkThrough.
public static Iterator walkThrough(final Object[] objs) {
return new Iterator() {
private int Pos = 0;
public Boolean hasNext() {
return (Pos< objs.length);
}
public Object next() throws NoSuchElementException {
if (Pos>= objs.length) throw NoSuchElementException;
else return objs[POs++];
}
public void remove() {
//not allowed here, but it's part of interface Iterator
//so we must implement it. Throw an exception:
throw new UnsupportedOperationException();
}
};
//Note the ";" above - it terminates the (complex) return-statement
}
The anonymous class is defined in the new expression itself. Because Iterator is an interface the anonymous class implicitly implements Iterator and the only possible constructor is the default no-args constructor. This solution is stretching the rule "only 5-10 lines" - but it's still rather simple and has well defined purpose (return an Iterator object).