嵌套类(nested class)是指被定义在另一个类的内部的类。嵌套类的目的应该只是为它的外围类(enclosing class)提供服务。如果嵌套类将来可能会用于其他某个环境中,它就应该是顶层类,而不是被设计为嵌套类。嵌套类分如下四种:
静态成员类(static member class)、非静态成员类(nostatic member class)、匿名类(anonymous class)和局部类(local class)。
除了静态成员类之外,其他三种都被称为内部类(Inner Class)。静态成员类和非静态成员类称为成员类,后两个称为非成员类。
10.1 创建内部类
package Ch10;
public class Parcel2 {
class Contents{
private int i=11;
public int value(){return i;}
}
public Contents contents(){
return new Contents();
}
class Destination{
private String label;
Destination(String whereTo){
label = whereTo;
}
String readLabel(){return label;}
}
public Destination to(String s){
return new Destination(s);
}
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();
//outter.inner 指明类
Parcel2.Contents c = q.contents();
Parcel2.Destination d = q.to("Borneo");
System.out.println(d.readLabel());
}
}
/*
Tasmania
Borneo
*/
10.2 链接到外部类
内部类拥有外部类的所有元素的访问权。
因为:
当一个外围类创建一个内部类对象时,此内部类对象必定会秘密捕获一个指向那个外围对象的引用。
内部类只能在和外部类相关联的情况下创建(除了静态成员内部类)
package Ch10;
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){
items[next++]=x;
}
private class SequenceSelector implements Selector{
private int i=0;
//子类方法,即使方法名一样方法签名不一样,也是完全不一样的方法,并不是覆盖。
//方法签名包括:返回类型,方法名,参数。
//不能写成 boolean end(int i)
//继承或者实现不能缩小访问权限。interface默认public,所以不写public就默认是proteed,出错。
public boolean end(){ return i==items.length;}
public Object current(){return items[i];}
public void next(){ if(i<items.length) i++;}
}
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(i+"");
}
Selector selector = sequence.selector();
while(!selector.end()){
System.out.print(selector.current()+" ");
selector.next();
}
}
}
/*
0 1 2 3 4 5 6 7 8 9 */
10.3 使用.this and .new
outter.this 产生外部类对象的引用。
package Ch10;
public class DotThis {
void f(){
System.out.println("DotThis.f()");
}
public class Inner{
public DotThis outer(){
return DotThis.this;
}
}
public Inner inner(){return new Inner();}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}
DotThis.f()
外部了对象.new,创建内部类对象。
public class DotNew{
public class Inner{}
public static void main(String[] args){
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
10.4
package Ch10;
import sun.security.krb5.internal.crypto.Des;
interface Destination{
String readLabel();
}
interface Contents{
int value();
}
class Parcel4{
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");
}
}
内部类实现接口,来隐藏细节。
10.5 在方法和作用域内的内部类
局部内部类
10.6 匿名内部类
使用外部的变量必须声明为final
10.7 嵌套内部类(不是真的内部类)
不需要内部类对象和外部类对象之间有联系,可以将内部类声明为static。嵌套类。
意味着:
1)要创建嵌套类的对象,并不需要其外围类的对象。
2)不能从嵌套类的对象中访问非静态的外围类对象。
(可以通过创建外部类对象,间接访问)
所以,普通内部类不能有static数据和字段,也不能包含嵌套内部类。就是不能static。
10.7.1 接口内部的类
放到接口内部的任何类都是static的,只是将嵌套类放于接口的命名空间,并不违反接口的规则。
package Ch10;
public interface ClassInInteface {
void howdy();
class Test implements ClassInInteface{
public void howdy(){
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
Howdy!
10.7.2 从多层嵌套类中访问外部类成员
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():
mnabb.h();
}
}
10.8 为什么需要内部类
接口解决了多重继承的部分问题,内部类使多重继承方案完整。
每个内部类都可以独立地继承一个(接口的实现),所以不论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果拥有的是抽象类或者具体的类,而不是接口,只有内部类才能实现多重继承。
10.8.1 闭包和回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。
同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;
回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;
异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。
回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。
进一步展示了外围类实现一个接口与内部类实现此接口之间的区别。
package Ch10;
import static tools.Print.print;
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();}
}
// MyIncrement已经有了一个不同的increment()方法,
// 如果Callee2继承了MyIncrement,就不能为了Incrementable而覆盖本身的increment()方法。
// 于是只能使用内部类独立实现Incremeabele。而且创建了内部类并没有在外围类的接口添加东西
// 也没有修改外围类的接口。
class Callee2 extends MyIncrement{
private int i =0;
public void increment(){
super.increment();
i++;
print(i);
}
//内部类实现此接口
private class Closure implements Incrementable{
public void increment(){
Callee2.this.increment();
System.out.println(Callee2.this);
System.out.println(this);
}
}
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);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
other operation
1
1
2
other operation
2
Ch10.Callee2@1540e19d
Ch10.Callee2$Closure@677327b6
other operation
3
Ch10.Callee2@1540e19d
Ch10.Callee2$Closure@677327b6
10.8.2 内部类和控制框架
control framework。
application framework 就是被设计用于解决某类特定问题的一个类或一组类。
Thinking in Patterns(with java)
control framework 是一类特殊的应用程序框架,解决响应事件的需求。主要用来响应事件的系统,被称为事件驱动系统。
10.9 内部类的继承
指向外围类对象的“秘密”引用必须初始化,而在导出类中不再存在可连接的默认对象。
package Ch11;
class WithInner{
class Inner{
}
}
public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
10.10 内部类可以被覆盖吗
当继承某个外围类时,这两个内部类时完全独立的两个实体,各自在自己的命名空间里。
//明确继承某个内部类
package Ch11;
import static tools.Print.print;
class Egg2{
protected class Yolk{
public Yolk(){ print("Egg2.Yolk()");}
public void f(){print("Egg2.Yolk.f()");}
}
private Yolk y = new Yolk();
public Egg2() {print("new Egg2()");}
public void insertYolk(Yolk yy){y=yy;}
public void g(){y.f();}
}
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk(){print("BigEgg2.Yolk()");}
public void f(){print("BigEgg2.Yolk().f()");}
}
public BigEgg2(){insertYolk(new Yolk());}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
output:
//先加载BigEgg2的基类,初始化其域。new folk() 语句打印
Egg2.Yolk()
// 再调用基类构造器,打印
new Egg2()
//初始化导出类域,再调用导出类构造函数,public BigEgg2(){insertYolk(new Yolk());}
// 调用基类构造器,Egg2中Yolk构造器,打印
Egg2.Yolk()
// 再调用导出类中Yolk构造器,打印
BigEgg2.Yolk()
//将导出类Yolk向上转型,将导出类BigEgg2向上转型,Egg2 e2 = new BigEgg2();
// e2.g();导出类没有覆盖基类g(),调用基类g()。y.f(),调用导出类f(),打印
BigEgg2.Yolk().f()
10.11 局部内部类
不能有访问说明符。
需要不止一个内部类对象,或者需要一个已经命名的构造器或者需要重载构造器,而匿名内部类只能用于初始化一个内部类对象。
interface Counter(){
int next();
}
public class local LocalInnerClass{
private int count = 0;
Counter getCounter(final String name){
//外部类中被局部内部类使用的变量必须为final
class LocalCounter implements Counter{
public LocalCounter(){
print("LocalCounter");
}
public int next(){
priintnb(name);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2(final String name){
// 看似返回了一个接口的实例,实则因为匿名内部类的特殊语法,返回的是实现了这个接口的一个实例
return new Counter(){
{
print("Counter()");
}
public int next(){
print(name);
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<2; i++)
print(c1.next());
for(int i=0; i<2; i++){
print(c2.next());}
}
}
output:
LocalCounter();
Counter();
Local Inner 0
Local Inner 1
Anonymous inner 2
Anonymous inner 3
10.12 内部类标识符
每个类都会产生.class文件。
内部类: 外围类名字,加上”$”,加上内部类的名字。
匿名内部类,编译器产生一个数字作为其标识符。
内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。