thinking-in-java(10)内部类

【0】开场白
1)内部类:将一个类的定义放在另一个类的定义内部,这个类就是内部类;
2)内部类优点:匿名内部类的一个优点就是可以将解决问题的代码隔离,聚拢在一点;

【10.1】创建内部类
【荔枝】把类的定义置于外围类的里面
public class Parcel1 {
	class Contents { // 内部类
		private int i = 11;

		public int value() {
			return i;
		}
	}

	class Destination { // 内部类
		private String label;

		Destination(String whereTo) {
			label = whereTo;
		}

		String readLabel() {
			return label;
		}
	}

	// Using inner classes looks just like
	// using any other class, within Parcel1:
	//  
	public void ship(String dest) {
		Contents c = new Contents(); // 
		Destination d = new Destination(dest);
		System.out.println(d.readLabel());
	}

	public static void main(String[] args) {
		Parcel1 p = new Parcel1();
		p.ship("Tasmania");
	}
} 
/*
Tasmania
*/ 
【荔枝】外部类有一个方法, 该方法返回一个指向内部类的引用, 如下:
public class Parcel2 {
	class Contents { // 内部类
		private int i = 11;
		public int value() { return i; }
	}

	class Destination {  // 内部类
		private String label;
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() { return label; }
	}

	public Destination to(String s) { // 外部类中的方法 返回一个指向 内部类的引用
		return new Destination(s);
	}
	public Contents contents() { // 外部类中的方法 返回一个指向 内部类的引用
		return new Contents();
	}
	public void ship(String dest) {
		Contents c = contents();
		Destination d = to(dest);
		System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		Parcel2 p = new Parcel2();
		p.ship("Tasmania");
		
		Parcel2 q = new Parcel2();
		// Defining references to inner classes:
		Parcel2.Contents c = q.contents(); // 注意它调用内部类的方式 是 Parcel2.Contents
		Parcel2.Destination d = q.to("Borneo"); // // 注意它调用内部类的方式 是 Parcel2.Destination
	}
}
/*
Tasmania
*/
【补充】如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象, 那么必须像在main方法中那样,具体指明这个对象的类型: OuterClassName.InnerClassName;

【10.2】链接到外部类
1)内部类拥有访问外围类的所有元素的访问权;

【荔枝】基于内部类实现迭代器设计模式
interface Selector {
	boolean end();
	Object current();
	void next();
}
public class Sequence {
	private Object[] items;
	private int next = 0;

	public Sequence(int size) {  
		items = new Object[size]; 
	}
	public void add(Object x) {
		if (next < items.length)
			items[next++] = x;
	}
	// 迭代器设计模式
	private class SequenceSelector implements Selector { // 内部类 访问外部类的 items 实例变量
		private int i = 0;

		public boolean end() { return i == items.length; }
		public Object current() { return items[i]; }
		public void next() {
			if (i < items.length)
				i++;
		}
		public void reverseSelector() { }
	}
	public Selector selector() { // 外部类方法 创建内部类并返回该实例.
		return new SequenceSelector();
	}
	public static void main(String[] args) {
		Sequence sequence = new Sequence(10);
		for (int i = 0; i < 10; i++)
			sequence.add(Integer.toString(i));
		Selector selector = sequence.selector();
		while (!selector.end()) {
			System.out.print(selector.current() + " ");
			selector.next();
		}
	}
} /*
 * Output: 0 1 2 3 4 5 6 7 8 9
 */// :~
【补充】当某个外围类的对象创建一个内部类对象时,此内部类对象必定会秘密捕获一个指向那个外围类对象的引用;然后,在你访问此外围类的成员时, 就是用那个引用来选择外围类的成员;

【10.3】使用.this(生成对外部类对象的引用) 与 .new(在 new 表达式中提供对其他外部类对象的引用)
【荔枝】如何使用 .this
public class DotThis {
	void f() {
		System.out.println("DotThis.f()");
	}

	public class Inner { // 内部类
		public DotThis outer() {
			return DotThis.this; // key: 生成对外部类对象的引用.
		}
	}

	public Inner inner() {
		return new Inner();
	}

	public static void main(String[] args) {
		DotThis dt = new DotThis();
		
		DotThis.Inner dti = dt.inner();
		dti.outer().f();		
	}
} /*
 * Output: DotThis.f()
 */// :~  
【荔枝】如何使用.new
 public class DotNew {
	public class Inner { }

	public static void main(String[] args) {
		DotNew dn = new DotNew();
		// .new : 在new 表达式中 提供对其他外部类对象的引用, 使用 .new 语法.
		DotNew.Inner dni = dn.new Inner(); 
	}
	
	public void main2() {
		DotThis dt = new DotThis();
		DotThis.Inner inner = dt.new Inner();
		System.out.println(inner);
	}
} // /:~
【补充】
补充1)在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上;
补充2)如果创建的是静态内部类, 则静态内部类不需要对外部类对象的引用;

【荔枝】.new 应用于 Parcel
public class Parcel3 {
	class Contents { // 内部类
		private int i = 11;

		public int value() {
			return i;
		}
	}

	class Destination { // 内部类
		private String label;

		Destination(String whereTo) {
			label = whereTo;
		}

		String readLabel() {
			return label;
		}
	}

	public static void main(String[] args) {
		Parcel3 p = new Parcel3();
		// Must use instance of outer class
		// to create an instance of the inner class:
		// 必须使用外部类对象实例 创建 内部类实例
		Parcel3.Contents c = p.new Contents();
		Parcel3.Destination d = p.new Destination("Tasmania");
	}
} // /:~ 
【10.4】内部类与向上转型
1)当将内部类向上转型为其基类, 尤其转型为一个接口时,内部类就有了用武之地;

【荔枝】内部类向上转型
// 荔枝:内部类向上转型
class Parcel4 {
	// private 访问修饰符 的内部类 隐藏子类的实现细节.
	private class PContents implements Contents {
		private int i = 11;
		public int value() { return i; }
	}

	protected class PDestination implements Destination {
		private String label;

		private PDestination(String whereTo) { label = whereTo; }
		public String readLabel() { 
			return label; 
		}
	}
	public Destination destination(String s) {
		return new PDestination(s);
	}
	public Contents contents() {
		return new PContents();
	}
}
public class TestParcel {
	public static void main(String[] args) {
		Parcel4 p = new Parcel4();
		Contents c = p.contents(); // 内部类向上转型.
		Destination d = p.destination("Tasmania");
	}
}  
【补充】
补充1)注意private内部类的访问权限;

【10.5】在方法和作用域内的内部类
1)可以在一个方法里面或在任意的作用域内定义内部类;

【荔枝】局部内部类:在方法作用域内创建一个完整的类
// 局部内部类的经典荔枝
public class Parcel5 {
	public Destination destination(String s) {
		class PDestination implements Destination { // 局部内部类,在方法中进行定义
			private String label;

			private PDestination(String whereTo) {
				label = whereTo;
			}

			public String readLabel() {
				return label;
			}
		}
		return new PDestination(s); // 在同一个方法中 返回 局部内部类的实例
	}

	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Destination d = p.destination("Tasmania");
	}
} // 
【荔枝】如何在任意的作用域内嵌入一个内部类
// 荔枝:在任意作用域中嵌入一个内部类.
public class Parcel6 {
	private void internalTracking(boolean b) {
		if (b) { 
			class TrackingSlip { // if 条件语句中 定义 局部内部类
				private String id;

				TrackingSlip(String s) {
					id = s;
				}

				String getSlip() { return id; }
			}
			TrackingSlip ts = new TrackingSlip("slip");
			String s = ts.getSlip();
			System.out.println(s);
		}	
		// 在 定义 TrackingSlip 的作用域之外 创建 TrackingSlip 实例是不可行的.
//		 TrackingSlip instance = new TrackingSlip("str"); // syntax error.
	}
	public void track() {
		internalTracking(true);
	}
	public static void main(String[] args) {
		Parcel6 p = new Parcel6();
		p.track();
	}
}  
【10.6】匿名内部类
1)匿名内部类荔枝:
public interface Contents {
	int value();
}  
// 荔枝-匿名内部类
public class Parcel7 {
	public Contents contents() {
		return new Contents() { // 插入一个类的定义 == 匿名内部类
			private int i = 11;

			public int value() {
				return i;
			}
		}; // 匿名内部类需要分号.
	}

	public static void main(String[] args) {
		Parcel7 p = new Parcel7();
		Contents c = p.contents();
	}
} 
【补充】
补充1)contents方法将返回值的生成与表示这个返回值的类的定义结合在一起;
补充2)匿名内部类语法说明: 创建一个继承自Contents的匿名类的对象; 通过new表达式返回的引用被自动向上转型为 对 Contents的引用;

【荔枝】上述匿名内部类(Parcel7.java)的语法是以下代码的简化版本,如下:
// 匿名内部类的等同版本(不过本版本要比匿名内部类复杂得多)
public class Parcel7b {
	// 创建一个继承自 Contents 的 匿名类的对象
	class MyContents implements Contents {
		private int i = 11;

		public int value() {
			return i;
		}
	}
	// 创建方法返回的引用被自动 向上转型为 对 Contents 的 引用
	public Contents contents() {
		return new MyContents();
	}

	public static void main(String[] args) {
		Parcel7b p = new Parcel7b();
		Contents c = p.contents();
	}
}  
以上代码使用了默认构造器来生成 Contents对象, 如果构造器是有参数的, 怎么办?

【荔枝】基于有参构造器定义匿名内部类
public class Wrapping {
	private int i;
	public Wrapping(int x) { i = x; }
	public int value() { return i; }
}  
// 荔枝-基于有参构造器 定义匿名内部类
public class Parcel8 {
	public Wrapping wrapping(int x) {
		return new Wrapping(x) { // 传递给有参构造器. 
			public int value() {
				return super.value() * 47; // super.value() 是基类方法返回值
			}
		}; // 匿名内部类需要分号
	}

	public static void main(String[] args) {
		Parcel8 p = new Parcel8();
		Wrapping w = p.wrapping(10);
		System.out.println(w.value());
	}
} 
【荔枝】在匿名内部类中定义字段时, 可以对其执行初始化操作
// 荔枝-在匿名内部类中定义字段时, 可以对其执行初始化操作
public class Parcel9 {
	// 希望 匿名内部类 使用一个 在其外部定义的 对象,其参数引用必须为final
	public Destination destination(final String dest) { 
		return new Destination() {
			private String label = dest;
			
			public String readLabel() { return label; }
		}; // 需要分号
	}
	public static void main(String[] args) {
		Parcel9 p = new Parcel9();
		Destination d = p.destination("Tasmania");
		System.out.println(d.readLabel());
	}
	
	public static void f1() {}
	public void f2(){ f1(); }
} 
/*
Tasmania
*/ 
【补充】希望 匿名内部类 使用一个 在其外部定义的 对象,其参数引用必须为final

在匿名内部类中不可能有命名构造器(因为它根本没名字)。但通过实例初始化, 就能够达到为匿名内部类创建一个构造器的效果,就像这样:
【荔枝】通过实例初始化为匿名内部类创建一个构造器
// 荔枝-通过实例初始化为匿名内部类创建一个构造器
abstract class Base {
	public Base(int i) {  print("Base constructor, i = " + i); }
	public abstract void f();
}
public class AnonymousConstructor {
	public static Base getBase(int i) {
		// 通过实例初始化, 就能够达到为 匿名内部类创建一个构造器的效果.
		return new Base(i) {
			{
				print("Inside instance initializer");
			}
			public void f() {
				print("In anonymous f(), and i = " + i);
			}
		};
	}
	public static void main(String[] args) {
		Base base = getBase(47);
		base.f();
	}
}  
/*
Base constructor, i = 47
Inside instance initializer
In anonymous f(), and i = 47
*/ 
【补充】在上述荔枝中, 不要求变量i一定是final的。因为 i 被传递给匿名类的基类构造器, 他并不会在匿名内部类的内部被直接使用;

【荔枝】为内部类字段进行赋值,则方法参数必须是 final
// 荔枝-为内部类字段进行赋值, 则方法参数 必须是 final
public class Parcel10 {	
	//  为内部类字段进行赋值, 则方法参数 必须是 final
	public Destination destination(final String dest, final float price) {
		return new Destination() {
			private int cost;
			// 对每个对象进行初始化
			{
				cost = Math.round(price); // 四舍五入
				if (cost > 100)
					System.out.println("Over budget!");
			}
			private String label = dest;

			public String readLabel() {
				return label;
			}
		};
	}

	public static void main(String[] args) {
		Parcel10 p = new Parcel10();
		Destination d = p.destination("Tasmania", 101.395F);
		System.out.println(d.readLabel());
	}
}
/*
Over budget!
Tasmania
*/ 
【补充】匿名内部类与正规的继承相比有些受限:因为匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备。如果实现接口,也只能实现一个接口;

【10.6.1】在访工厂方法
【荔枝】基于匿名内部类的工厂方法
interface Service {  
	void method1(); 
	void method2(); 
}
interface ServiceFactory { 
	Service getService(); 
}
// 荔枝-通过匿名内部类 实现工厂方法模式(经典荔枝)
class Implementation1 implements Service {
	private Implementation1() { }
	public void method1() { print("Implementation1 method1"); }
	public void method2() { print("Implementation1 method2"); }

	public static ServiceFactory factory = new ServiceFactory() { // 静态匿名内部类
		public Service getService() { return new Implementation1(); }
	};
}
class Implementation2 implements Service {
	private Implementation2() { }
	public void method1() { print("Implementation2 method1"); }
	public void method2() { print("Implementation2 method2"); }

	public static ServiceFactory factory = new ServiceFactory() { // 静态匿名内部类
		public Service getService() { return new Implementation2(); }
	};
}
public class Factories {
	public static void serviceConsumer(ServiceFactory fact) {
		Service s = fact.getService();
		s.method1();
		s.method2();
	}

	public static void main(String[] args) {
		serviceConsumer(Implementation1.factory);
		serviceConsumer(Implementation2.factory);
	}
} /*
 * Output: 
 * Implementation1 method1 
 * Implementation1 method2 
 * Implementation2 method1
 * Implementation2 method2
 */// :~  
【荔枝】通过匿名内部类 实现工厂方法模式(经典荔枝)
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
//通过匿名内部类 实现工厂方法模式(经典荔枝)
class Checkers implements Game {
	private Checkers() {}
	private int moves = 0;
	private static final int MOVES = 3;

	public boolean move() {
		print("Checkers move " + moves);
		return ++moves != MOVES;
	}
	public static GameFactory factory = new GameFactory() { // 匿名内部类.
		public Game getGame() { return new Checkers(); }
	};
}
class Chess implements Game {
	private Chess() {}
	private int moves = 0;
	private static final int MOVES = 4;
	public boolean move() {
		print("Chess move " + moves);
		return ++moves != MOVES;
	}
	public static GameFactory factory = new GameFactory() { // 匿名内部类.
		public Game getGame() { return new Chess(); }
	};
}
public class Games {
	public static void playGame(GameFactory factory) {
		Game s = factory.getGame();
		while (s.move()) ;
	}
	public static void main(String[] args) {
		playGame(Checkers.factory);
		playGame(Chess.factory);
	}
}  
/*
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*/ 
【10.7】嵌套类(静态内部类)
1)静态内部类意味着:
1.1)要创建匿名内部类的对象,并不需要其外围类对象;
1.2)不能从匿名内部类的对象中访问非静态的外围类对象;
2)静态内部类与普通内部类的区别:普通内部类的字段与方法, 只能放在类的外部层次上, 所以普通内部类不能有static数据和static字段,也不能包含 静态内部类。 但是静态内部类里是可以包含所有这些东西的;

【荔枝】静态内部类可以包含static数据,static字段和方法,也可以包含普通的字段和方法
// 嵌套类(静态内部类)的荔枝
public class Parcel11 {
	// 静态内部类
	private static class ParcelContents implements Contents {
		private int i = 11;
		public int value() { return i; }
	}
	// 静态内部类
	protected static class ParcelDestination implements Destination {
		private String label; // 普通变量

		private ParcelDestination(String whereTo) {
			label = whereTo;
		}
		public String readLabel() { // 普通方法
			return label;
		}
		public static void f() { } // 静态方法
		static int x = 10; // 静态变量
		static class AnotherLevel { // 静态内部类( 嵌套类 )
			public static void f() { } // 静态方法
			static int x = 10; // 静态变量
			int y = 10; // 普通变量
		}
		class A { // 普通内部类
			class B {
				class C { }
			}
		}
	}
	public static Destination destination(String s) {
		return new ParcelDestination(s);
	}
	public static Contents contents() {
		return new ParcelContents();
	}
	public static void main(String[] args) {
		Contents c = contents();
		Destination d = destination("Tasmania");
	}
}  
【10.7.1】接口内部的类
1)正常情况下, 不能在接口内部放置任何代码,但静态内部类可以作为接口的一部分;
2)放置到接口中的任何类都默认是 public static;

【荔枝】在接口内部定义静态内部类
// 在接口作用域内放置 嵌套类(静态内部类)
// 接口中的类 自动是 public 和 static 的.
interface ClassInInterface{
	void howdy();
	class Test implements ClassInInterface { // 默认是 public static
		public void howdy() {
			System.out.println("Howdy!");
		}
		public static void test() {
			System.out.println("my name is test.");
		}
		public static void main(String[] args) {
			new Test().howdy(); 
		}
	}
}
/*
错误: 找不到或无法加载主类 chapter10.ClassInInterfaceTest$Test
*/ 
【补充】在每个类中都写main方法来测试。这样做有一个缺点: 那就是必须带着那些已经编译过的额外代码。如果这对你是个麻烦,那就可以使用匿名内部类来放置测试代码;
//荔枝-使用匿名内部类来放置测试代码
public class TestBed {
	public void f() {
		System.out.println("f()");
	}
	public static class Tester {
		public static void main(String[] args) {
			TestBed t = new TestBed();
			t.f();
		}
	}
} /*
 * Output: f()
 */ 
【说明】这生成了一个独立的类 TestBed$Tester(要运行这个程序, 执行 java TestBed$Tester 即可);可以使用这个类来做测试, 但不必再发布的产品中包含它, 在将产品打包前可以简单地删除 TestBed$Tester.class;
【10.7.2】从多层嵌套类中访问外部类的成员
1)一个内部类被嵌套多少层并不重要:它能透明地访问所有它所嵌入的外围类的所有成员;

【荔枝】从多层嵌套类中访问外部类的成员
 // 荔枝-从多层嵌套类中访问外部类的成员 
class MNA {
	private void f() {}
	class A {
		private void g() {}
		public class B {
			// 从多层嵌套类中 访问外部类的成员.
			void h() {
				g(); // 调用 A.g()
				f(); // 调用 MNA.f()
			}
		}
	}
}

public class MultiNestingAccess {
	public static void main(String[] args) {
		MNA mna = new MNA();
		MNA.A mnaa = mna.new A(); // .new 表达式提供对其他外部类对象的引用.
		MNA.A.B mnaab = mnaa.new B();
		mnaab.h();
	}
} // /:~ 
【10.8】为什么需要内部类?
1)内部类最吸引人的原因: 每个内部类都能独立继承自一个(接口的)实现, 所以无论外围类是否已经继承了某个(接口的)实现, 对于内部类都没有影响;
2)内部类使得多重继承的解决方案变得完整。接口解决了多重继承的部分问题, 内部类有效地实现了 多重继承。也就是说, 内部类允许继承多个非接口类型(译注:类或抽象类);

3)考虑以下情形:必须在一个类中以某种方式实现两个接口。 有两种实现方式:使用单一类, 或者使用内部类;
// 荔枝-实现多重接口的荔枝
// 方式1-使用单一类
class X implements A, B {}

// 方式2-使用内部类, 如下:
// 外部类本身实现一个接口;
// 外部类的方法返回一个匿名内部类(匿名内部类就是一个接口类型), 以达到实现两个接口的目的;
class Y implements A {
	B makeB() {
		// 匿名内部类
		return new B() {};
	}
}
public class MultiInterfaces {
	static void takesA(A a) {}
	static void takesB(B b) {}

	public static void main(String[] args) {
		X x = new X();
		Y y = new Y();
		takesA(x);
		takesA(y);
		takesB(x);
		takesB(y.makeB()); // this line.(bingo)
	}
}  
4)如果拥有的是抽象类或具体类,而不是接口, 那就只能使用内部类才能实现多重继承;
(干货——使用内部类才能实现多重继承)

【荔枝】使用内部类才能实现多重继承
class D {} // 具体类
abstract class E {} // 抽象类

// 荔枝-使用内部类才能实现多重继承
class Z extends D {
	E makeE() {
		return new E() {};
	}
}

public class MultiImplementation {
	static void takesD(D d) {}
	static void takesE(E e) {}

	public static void main(String[] args) {
		Z z = new Z();
		takesD(z);
		takesE(z.makeE());
	}
} 
5)内部类有以下特性:
特性1)内部类可以有多个实例, 每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立;
特性2)在单个外围类中, 可以让多个内部类以不同方式实现同一个接口,或继承同一个类;
特性3)创建内部类对象的时刻并不依赖于外围类对象的创建;
特性4)内部类并没有 is-a 关系, 他就是一个独立实体;

【10.8.1】闭包与回调
1)闭包是一个可调用的对象,他记录了一些信息,这些信息来自于创建它的作用域;
2)内部类是面向对象的闭包: 因为内部类不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员;

【荔枝】通过内部类提供闭包功能
// 荔枝-通过内部类提供闭包功能
interface Incrementable {  void increment(); }
// 类本身实现接口
class Callee1 implements Incrementable {
	private int i = 0;
	public void increment() { i++; print(i); }
}

class MyIncrement {
	public void increment() { print("Other operation"); }
	static void f(MyIncrement mi) { mi.increment(); }
}
// 类的内部类实现接口
class Callee2 extends MyIncrement {
	private int i = 0;
	@Override
	public void increment() {
		super.increment();  i++; print(i);
	}
	// 闭包内部类
	private class Closure implements Incrementable {
		public void increment() {
			// 指定外部类方法,否则你将得到一个无限循环.
			Callee2.this.increment(); // 返回外部类对象的引用(钩子),利用钩子调用外部类的方法,称为回调
		}
	}
	// 返回回调引用
	Incrementable getCallbackReference() {  return new Closure(); }
}
class Caller {
	private Incrementable callbackReference;
	Caller(Incrementable cbh) { callbackReference = cbh; }
	void go() { callbackReference.increment(); }
}
public class Callbacks {
	public static void main(String[] args) {
		Callee1 c1 = new Callee1();
		Callee2 c2 = new Callee2();
		MyIncrement.f(c2); // Other operation 1
		
		Caller caller1 = new Caller(c1);
		Caller caller2 = new Caller(c2.getCallbackReference()); // 获得回调引用
		caller1.go(); // 1 
		caller1.go(); // 2
		caller2.go(); // Other operation 2
		caller2.go(); // Other operation 3
	}
}  
/*
Other operation
1
1
2
Other operation
2
Other operation
3
*/ 
分析1)内部类Closure 实现了 Incrementable接口,以提供返回 Callee2的钩子;
分析2)Caller的构造器需要一个 Incrementable 的引用作为参数(虽然可以在任意时刻捕获回调引用),然后在以后的某个时刻,Caller对象可以使用此引用回调Callee类;

【10.9】内部类的继承
// 荔枝-内部类的继承
class WithInner {
	class Inner {}
}

public class InheritInner extends WithInner.Inner {
	// InheritInner() {} // 这个无参构造器 无法编译,Won't compile
	
	InheritInner(WithInner wi) {
		wi.super();
	}

	public static void main(String[] args) {
		WithInner wi = new WithInner();
		InheritInner ii = new InheritInner(wi);
	}
} // /:~ 
分析1)InheritInner只继承自内部类,而不是外围类;
分析2)当要生成一个构造器时,默认的构造器并不算好,而且不能只是传递一个指向外围类对象的引用。
分析3)必须在构造器内使用如下语法: enclosingClassReference.super(); 这样才提供了必要的引用, 然后程序才能通过编译;

【10.10】内部类可以被覆盖吗?
1)内部类覆盖:如果创建一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?
2)覆盖内部类并不起什么作用;
// 荔枝-覆盖内部类(不起任何作用)
class Egg {
	private Yolk y;

	protected class Yolk {
		public Yolk() { print("Egg.Yolk()"); } // 2, 而是调用这个 Yolk 构造方法。
	}

	public Egg() {
		print("New Egg()"); // 1
		y = new Yolk();
	}
}

public class BigEgg extends Egg {
	// 内部类 BigEgg.Yolk 没有覆盖 内部类 Egg.Yolk 
	public class Yolk {
		public Yolk() { print("BigEgg.Yolk()"); } // not this one. 并没有调用这个 Yolk 构造方法。
	}

	public static void main(String[] args) {
		new BigEgg();
	}
}
/*
New Egg()
Egg.Yolk()
*/ 
3)明确地继承某个内部类是奏效的, 如下:
// 荔枝-明确地继承某个内部类是奏效的
class Egg2 {
	protected class Yolk {
		public Yolk() { print("Egg2.Yolk()"); } // 1, 3
		public void f() { print("Egg2.Yolk.f()"); }
	}
	private Yolk y = new Yolk();

	public Egg2() { print("New Egg2()"); } // 2
	public void insertYolk(Yolk yy) { y = yy; }
	public void g() { y.f(); }
}

public class BigEgg2 extends Egg2 {
	// 内部类 BigEgg2.Yolk 明确继承继承 另一个外部类的内部类 Egg2.Yolk
	public class Yolk extends Egg2.Yolk {
		public Yolk() { print("BigEgg2.Yolk()"); } // 4
		public void f() { print("BigEgg2.Yolk.f()"); } // 5
	}
	public BigEgg2() { insertYolk(new Yolk()); }

	public static void main(String[] args) {
		Egg2 e2 = new BigEgg2();
		e2.g();
	}
}
/*
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*/ 
【10.11】局部内部类
1)介绍: 局部内部类不能有访问说明符,因为它不是外围类的一部分;但是他可以访问当前代码块内的常量,以及此外围类的所有成员;
2)对局部内部类与匿名内部类的创建进行了比较,荔枝如下:
// 荔枝-局部内部类
// 荔枝-对局部内部类与匿名内部类的创建进行了比较
interface Counter { int next(); }
public class LocalInnerClass {
	private int count = 0;

	Counter getCounter(final String name) {
		class LocalCounter implements Counter { // 方法域中声明 局部内部类
			public LocalCounter() { print("LocalCounter()"); }

			public int next() {
				printnb(name); 
				return count++; // 共同操作 外部类的字段
			}
		}
		return new LocalCounter();
	}
	Counter getCounter2(final String name) {
		return new Counter() { // 匿名内部类完成 与 局部内部类相同的工作
			{ print("Counter()"); }
			public int next() {
				printnb(name); 
				return count++; // 共同操作 外部类的字段
			}
		};
	}
	public static void main(String[] args) {
		LocalInnerClass lic = new LocalInnerClass();
		Counter c1 = lic.getCounter("Local inner "), 
				c2 = lic.getCounter2("Anonymous inner ");
		for (int i = 0; i < 5; i++)
			print(c1.next());
		for (int i = 0; i < 5; i++)
			print(c2.next());
	}
} 
/*
LocalCounter()
Counter()
Local inner 0
Local inner 1
Local inner 2
Local inner 3
Local inner 4
Anonymous inner 5
Anonymous inner 6
Anonymous inner 7
Anonymous inner 8
Anonymous inner 9
*/ 
【补充】为什么有些时候仍然使用局部内部类,不是已经有匿名内部类了吗?
理由1)需要一个已命名的构造器,或者需要重载构造器,而匿名内部类只能用于实例化;
理由2)需要不止一个该内部类的对象;

【10.12】内部类标识符
1)内部类生成一个 .class文件以包含他们的 Class 对象信息;
2)这些类文件的命名有严格的规则: 外围类的名字, 加上 $ , 再加上内部类的名字;
3)荔枝: LocalInnerClass.java 生成的 .class 文件包括:
Counter.class // 接口
LocalInnerClass$1.class // 匿名内部类 
LocalInnerClass$1LocalCounter.class // 局部内部类
LocalInnerClass.class // LocalInnerClass类


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值