第10章 内部类

可以将一个类定义放在另一个类的定义内部,这就是内部类。内部类了解外围类,并能与之通信。
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体指明这个对象的类型:OuterClassName.InnerClassName。
当生成一个内部类的对象时,此对象与制造它的外围对象(enclosing object)之间就有了一种联系,所以它能访问外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。有时你可能想要告知某些其他对象,去创建其某个内部类的对象。要实现此目的,你必须在new表达式中提供对其他外部类对象的引用,这是需要使用.new语法。
在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。但是,如果创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用。
可以在一个方法里面或者在任意的作用域内定义内部类。这么做有两个理由:
1)如前所示,你实现了某类型的接口,于是可以创建并返回对其的引用。
2)你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
内部类可以实现:
1)一个定义在方法中的类。
2)一个定义在作用域内的类,此作用域在方法的内部。
3)一个实现了接口的匿名类。
4)一个匿名类,它扩展了有非默认构造器的类。
5)一个匿名类,它执行字段初始化。
6)一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)。
方法内部类中,外围对象不能访问该内部类,因为该内部类只是外围类某个方法的一部分,而不是外围类的一部分。作用域内部类中,外围对象也不能访问该内部类,只有在定义该内部类的作用域中才可访问。
创建一个继承自某接口的匿名类的对象。通过new表达式返回的引用被自动向上转型为对该接口的引用。
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。如果只是在基类构造器中使用,则可以不添加final。对于匿名类而言,实例初始化的实际效果就是构造器。
匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。
将内部类声明为static,称为嵌套类。普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。然而,当内部类时static的时,意味着:
1)要创建嵌套类的对象,并不需要其外围类的对象。
2)不能从嵌套类的对象中访问非静态的外围类对象。
普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。

class MNA {
	private void f() {}
	class A {
		private void g() {}
		public class B {
			void h() {
				g();
				f();
			}
		}
	}
}

public class MultiNestingAccess {
	public static void main(String[] args) {
		MNA mna = new MNA();
		MNA.A mnaa = mna.new A();
		MNA.A.B mnaab = mnaa.new B();
		mnaab.h();
	}
}

一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。
使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
内部类使得多重继承的解决方案变得完整,内部类允许继承多个非接口类型(类或抽象类)。

interface A {}
interface B {}

class X implements A, B {}

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());
	}
}

如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。

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());
	}
}

内部类其他一些特性:
1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
3)创建内部类对象的时刻并不依赖于外围类对象的创建。
4)内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。
应用程序框架(application framework)就是被设计用以解决某类特定问题的一个类或一组类。
内部类允许:
1)控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装了起来。内部类用来表示解决问题所需的各种不同的action().
2)内部类能够很容易地访问外围类的任意成员,所以可以避免这种实现变得笨拙。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值