Chapter 5 Nested Classes and Interfaces

Classes’ fields are default to be protected, and the modifier is omitted implicitly.

1) A nested type is considered a part (member) of its enclosing type and the two share a trust relationship in which each can access all members of the other.

  The ability to define nested types serves two main purposes: (1) nested classes and nested interfaces allow types to be structured and scoped into logically related groups; (2) more important, nested classes can be used to connect logically related objects simply and effectively(appears extensively in AWT and JavaBeans).

 

  There’re two kinds of nested types: static nested types and non-static nested types. The former allows simple structuring of types; the latter defines a special relationship between a nested object and an object of the enclosing class.

Static Nested Types:

  A nested class or interface that is declared as a static member of its enclosing class or interface acts just like any non-nested, or top-level, class or interface, except that its name and accessibility are defined by its enclosing type. The name of a nested type is expressed as EnclosingName.NestedName. The nested type is accessible only if the enclosing type is accessible.

  Static nested types serve as a structuring and scoping mechanism for logically related types. However, static nested types are members of their enclosing type and as such can access all other members of the enclosing type including private ones through an appropriate object reference of course. This gives the nested type a special, privileged relationship with the enclosing type.

  Because static nested types are members of their enclosing type, the same accessibility rules apply to them as for other members. For classes this means that a static nested class or interface can have private, package, protected, or public access, while for interfaces, nested types are implicitly public.

Example 1:

// Enclosing class and Enclosed class could access each other's members, 
// including the private ones
class Enclosing { 
	private int value;
	
	Enclosing(int value) {
		this.value = value;
	}
	
	void print(Enclosed obj) {
		System.out.println("Enclosed: " + obj.key);
	}
	
	static class Enclosed {
		private int key;
		
		Enclosed(int key) {
			this.key = key;
		}
		
		void print(Enclosing obj) {
			System.out.println("Enclosing: " + obj.value);
		}
	}
}

public class StaticNested {
	public static void main(String[] args) {
		Enclosing a = new Enclosing(10);
		Enclosing.Enclosed b = new Enclosing.Enclosed(100);
		a.print(b);
		b.print(a);
	}
}

Output 1:

Enclosed: 100
Enclosing: 10

 

  When nested in an interface, a class declaration is always static and the modifier is omitted by convention(Because you cannot create an object of an interface). A static nested class acts just like any top-level class. It can extend any other class (including the class it is a member of), implement any interface and itself be used for further extension by any class to which it is accessible. It can be declared final or abstract, just as a top-level class can, and it can have annotations applied to it.

Example 2:

interface Tmp {
	private class InnerClass{ 
		// compiler error: The interface member type InnerClass could only be public
		
	}
}

  Static nested classes are members of their enclosing type. Static nested classes enclosed in an interface are implicitly public(But if the interface is invisible, there’s no way to access the enclosed class, too.); if enclosed by a class, you can declare them to be accessible in any way you like.

 

  There is no restriction on how a static nested class can be extendedit can be extended by any class to which it is accessible. Of course, the extended class DOES NOT inherit the privileged access that the nested class has to the enclosing class.

  Nested enum classes are always static, although by convention the static modifier is omitted from the enum declaration.

  Nested interfaces are also always static and again, by convention the static modifier is omitted from the interface declaration. (Because you could not create an object of an interface.)

 

Non-static Nested Type:

  Non-static nested classes are called inner classes. Non-static class members are associated with instances of a class -- non-static fields are instance variables and non-static methods operate on an instance. Similarly, an inner class is also (usually) associated with an instance of a class, or more specifically an instance of an inner class is associated with an instance of its enclosing classthe enclosing instance or enclosing object.

You often need to closely tie a nested class object to a particular object of the enclosing class.

Example:

public class BankAccount {
	private long number;
	private long balance;
	private Action lastAct;
	
	public class Action {
		private String act;
		private long amount;
		Action(String act, long amount) {
			this.act = act;
			this.amount = amount;
		}
		
		public String toString() {
			return number + ": " + act + " " + amount;
		}
	}
	
	public void deposit(long amount) {
		balance += amount;
		lastAct = this.new Action("deposit", amount);
		// equals to the implicit one: lastAct = new Action("deposit", amount);
	}
	
	public void withdraw(long amount) {
		balance -= amount;
		lastAct = this.new Action("withdraw", amount);
		// equals to the implicit one: lastAct = this.new Action("withdraw", amount);
	}
	
	public void transfer(BankAccount out, long amount) {
		out.withdraw(amount);
		this.deposit(amount);
		out.lastAct = out.new Action("transfer", amount);
		this.lastAct = this.new Action("transfer", amount);
	}
}

 

  The relationship between an Action object and its BankAccount object is established when the Action object is created, as shown in the deposit and withdraw methods. When an inner class object is created, it must be associated with an object of its enclosing class. Usually, inner class objects are created inside instance methods of the enclosing class, as in deposit and withdraw.

 

  A nested class can access all members of its enclosing class including private fields and methods without qualification because it is part of the enclosing class's implementation. The enclosing class can also access the private members of the inner class, but only by an explicit reference to an inner class object such as lastAct.

  While an object of the inner class is always associated with an object of the enclosing class, the converse is not true. An object of the enclosing class need not have any inner class objects associated with it, or it could have many.

Example:

public class BankAccount {
	private long number;
	private long balance;
	private Action lastAct;
	
	public class Action {
		private String act;
		private long amount;
		Action(String act, long amount) {
			this.act = act;
			this.amount = amount;
		}
		
		public String toString() {
			return number + ": " + act + " " + amount;
			// Equals to: return BankAccount.this.number + ": " + act + " " + amount;
		}
	}
	
	public void deposit(long amount) {
		balance += amount;
		lastAct = this.new Action("deposit", amount);
		// equals to the implicit one: lastAct = new Action("deposit", amount);
	}
	
	public void withdraw(long amount) {
		balance -= amount;
		lastAct = this.new Action("withdraw", amount);
		// equals to the implicit one: lastAct = this.new Action("withdraw", amount);
	}
	
	public void transfer(BankAccount out, long amount) {
		out.withdraw(amount);
		this.deposit(amount);
		out.lastAct = out.new Action("transfer", amount);
		this.lastAct = this.new Action("transfer", amount);
	}
	
	public static void main(String[] args) {
		// Action obj = new Action("deposit", 100);
		// Compile Error:
		// No enclosing instance of type BankAccount is accessible. 
		// Must qualify the allocation with an enclosing instance of type BankAccount 
		// (e.g. x.new A() where x is an instance of BankAccount).
		
		
	}
}

  When deposit creates an Action object, a reference to the enclosing BankAccount object is automatically stored in the new Action object. Using this saved reference, the Action object can always refer to the enclosing BankAccount object's number field by the simple name number, as shown in toString. The name of the reference to the enclosing object is this preceded by the enclosing class namea form known as qualified-this.

return BankAccount.this.number + ": " + act + " " + amount;

Similarly, qualified-super reference allows access to members of the enclosing instance’s superclass that have been hidden, or overridden, by the enclosing class. For example, given a class T that extends S, in an inner class of T, we can invoke the same implementation of m using T.super.m() in an expression -- a qualified-super reference -- and similarly for fields of S hidden by fields in T.

 

  A nested class can have its own nested classes and interfaces. References to enclosing objects can be obtained for any level of nesting in the same way: the name of the class and this. If class X encloses class Y which encloses class Z, code in Z can explicitly access fields of X by using X.this.

 

  An inner class can be extended just as any static nested class or top-level class can. The only requirement is that objects of the extended class must still be associated with objects of the original enclosing class or a subclass. Usually this is not a problem because the extended inner class is often declared within an extension of the outer class:

class Outer {
	class Inner{}
}


public class ExtendedOuter extends Outer{
	class ExtendedInner extends Inner{}
	Inner ref = new ExtendedInner();
}

  The ref field is initialized when an ExtendedOuter object is created. The creation of the ExtendedInner instance uses the default no-arg constructor of ExtendedInner, which in turn implicitly invokes the default no-arg constructor of Inner by using super. The constructor for Inner requires an object of Outer to bind to, which in this case is implicitly the current object of ExtendedOuter.

 

  If the enclosing class of the inner subclass is not a subclass of Outer, or if the inner subclass is not itself an inner class, then an explicit reference to an object of Outer must be supplied when the Inner constructor is invoked via super. For example:

class Unrelated extends Outer.Inner {
	Unrelated(Outer ref) {
		ref.super();
	}
}

  When the construction of an Unrelated object reaches the point where the superclass constructor is invoked, there must be an object of class Outer to which the superclass object can be bound. Since Unrelated is not itself an inner class of Outer, there is no implicit enclosing object. Similarly, because Unrelated is not a subclass of Outer, the current object of Unrelated is not a valid enclosing object. We must provide an explicit reference to an Outer object for the superclass object to bind to. We chose to supply that reference using an argument to the Unrelated constructor, which uses it as an explicit binding reference in the invocation of the superclass constructor.

 

2) An inner class method with the same name as an enclosing class method hides all overloaded forms of the enclosing class method, even if the inner class itself does not declare those overloaded forms. For example:

public class OuterClass {
	void print(){}
	void print(int v){}
	
	class Inner {
		void print(){}
		void show(){
			print(); //Inner.print()
			OuterClass.this.print(); // OuterClass.print()
			print(10); // Compile Error: Inner.print hides all the forms of OuterClass.print
		}
	}
}

3) You can define inner classes in code blocks, such as a method body, constructor, or initialization block. These local inner classes are not members of the class of which the code is a part but are local to that block, just as a local variable is. A local inner class can access all the variables that are in scope where the class is definedlocal variables, method parameters, instance variables (assuming it is a non-static block), and static variables. The only restriction is that a local variable or method parameter can be accessed only if it is declared final.

public static Iterator<Object>
    walkThrough(final Object[] objs) {

    class Iter implements Iterator<Object> {
        private int pos = 0;
        public boolean hasNext() {
            return (pos < objs.length);
        }
        public Object next() throws NoSuchElementException {
            if (pos >= objs.length)
                throw new NoSuchElementException();
            return objs[pos++];
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    return new Iter();
}

 

4) Anonymous Inner Classes:

public static Iterator<Object>
    walkThrough(final Object[] objs) {

    return new Iterator<Object>() {
        private int pos = 0;
        public boolean hasNext() {
            return (pos < objs.length);
        }
        public Object next() throws NoSuchElementException {
            if (pos >= objs.length)
                throw new NoSuchElementException();
            return objs[pos++];
        }
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
}

5)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章