10.内部类

1.内部类定义
将一个类的定义放在另一个类的定义内部,
内部类了解外围类,并能与之通信。

2. .this 和.new
.this返回外围类的引用。
public class DotThis {
  public class Inner {
    public DotThis outer() {
      return DotThis.this;
    }
  }

.new 创建内部类,如果是非静态的内部类,得先创建外围类,然后才是内部类。
public class DotNew {
  public class Inner {}
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
  }
}


3.内部类可以继承接口,可是写在外围类的作用域里,可以像成员变量一样设置private 到public 四个访问权限,普通内部类不能含有static数据和字段。

4.匿名内部类
这中类没有名字,只能通过继承和实现接口创建。
这是一个匿名类的普通创建方法。
public class Parcel7 {
  public Contents contents() {
    return new Contents() { // 这里我们可能会误以为是匿名类的名字是Contents,其实不是。
      private int i = 11;  
      public int value() { return i; }
    }; // Semicolon required in this case
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents();
  }
}
上面的语法相当于
public class Parcel7b {
  class MyContents implements Contents {
    private int i = 11;
    public int value() { return i; }
  }
  public Contents contents() { return new MyContents(); }
  public static void main(String[] args) {
    Parcel7b p = new Parcel7b();
    Contents c = p.contents();
  }
}
到此我们应当明白匿名内部类确实没有名字,只是继承,覆写和实现父类,这点事理解匿名内部类的关键。
没有名字,也没有构造器,可以说匿名内部类,是已存在的类和接口在某个外围类中的纯导出类,因为没有名字,都是通过父类和接口来控制导出类的动作,所以写父类中没有的动作是多余的。

同时 注意的是匿名内部类要用外围类的成员,得加final
5.嵌套类

静态内部类就是嵌套类。不能用.this
嵌套类可以写在接口内部,甚至实现外围接口。

5.1接口的内部类都自动为 public static. 域都是 public static final,方法都是public。

内部类不管嵌套多少层,都能透明的访问所有它的外围类的所有成员,直接用。

6.为什么需要内部类?
每个内部类都能独立继承一个(类或接口)的实现,不管外围类是否已经继承了哪个类,对内部类都没有影响。
如果实现的接口,以下X和Y没有区别

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
  B makeB() {
    // Anonymous inner class:
    return new B() {};
  }
}
如果都是A,B都是抽象类或具体的类,X显然不能实现多重继承,Y则可以。
内部类是多重继承的一个补充。

7.闭包和回调

这个问题得举个书上的例子,耐心看一下还是能理解的。
闭包:闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(“创建内部类的作用域”)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。

interface Incrementable {
void increment();
}
// Very simple to just implement the interface:
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
System.out.println(i);
}
}
class MyIncrement {
void increment() {
System.out.println("Other operation");
}
static void f(MyIncrement mi) { mi.increment(); }
}
// If your class must implement increment() in
// some other way, you must use an inner class:
class Callee2 extends MyIncrement {
private int i = 0;
private void incr() {
i++;
System.out.println(i);
}
private class Closure implements Incrementable {
public void increment() { incr(); }
}
Incrementable getCallbackReference() {
return new Closure();
}
}
class Caller {
private Incrementable callbackReference;
Caller(Incrementable cbh) { callbackReference = cbh; }
void go() { callbackReference.increment(); }
}
public class Callbacks {
private static Test monitor = new Test();
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();
monitor.expect(new String[] {
"Other operation",
"1",
"2",
"1",
"2"
});
}
} ///:~
这个例子进一步揭示了外围类实现一个接口与内部类实现此接口之间的区别。就代码而言,Callee1是简单的解决方式。Callee2继承自MyIncrement,后者已经有了一个不同的increment()方法,并且与Incrementable接口期望的increment()方法完全不相关。所以如果Callee2继承了MyIncrement,就不能为了使用Incrementable而重载increment()方法,于是你只能使用内部类独立地实现Incrementable。还要注意,当你创建了一个内部类时,你并没有把什么东西加入到外围类的接口中,也没有修改外围类的接口。
注意,在Callee2中除了getCallbackReference()以外,其它成员都是private的。要想建立与外部世界的任何连接,接口Incrementable都是必需的。在这里你可以看到,接口是如何与接口的实现完全独立的。
内部类Closure实现了Incrementable,以提供一个返回Callee2的“钩子(hook)”。无论谁获得此Incrementable的引用,都只能调用increment(),除此之外没有其它功能(不像指针那样,允许你做很多事情)
Caller的构造器需要一个Incrementable的引用作为参数(虽然可以在任意时刻捕获回调引用),然后在以后的某个时刻,Caller对象可以使用此引用回调Callee类。
回调的价值在于它的灵活性;你可以在运行期动态地决定需要调用什么方法。

这些是书上的例子,没有这些东西也不太好说清楚。
总结:实现指针,闭包中有外围类的引用,可以通过它了解外围类。
    如果类A继承类B,又要实现接口C,但B和C中有相同的方法,功能不一样,这时将C作为A的内部类,并给予返回引用。可以用两个方法,而不会混淆。

8.内部类与控制框架
设计模式将变化的事物与保持不变的事物分开,模板方法是保持不变的事物,而可覆盖的方法是变化的事物。

9.内部类的继承

如class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {//本类只想继承内部类,而不是外围类。
  //! InheritInner() {} // Won't compile
  InheritInner(WithInner wi) {     //但是外围类必须得初始化,传入父类
    wi.super();                    //并加入父类.super
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}

10.内部类覆盖

两个类中内部类名字虽然一样,但是它们是完全独立的两个实体,各自在自己的命名空间,明确继承是可以的。
class Egg {
  private Yolk y;
  protected class Yolk {
    public Yolk() { print("Egg.Yolk()"); }
  }
  public Egg() {
    print("New Egg()");
    y = new Yolk();
  }

public class BigEgg extends Egg {
  public class Yolk {           //覆盖不了,如果覆盖可以写成 public class Yolk extends Egg.Yolk
    public Yolk() { print("BigEgg.Yolk()"); }
  }
  public static void main(String[] args) {
    new BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~

11.局部内部类
就是写在外围类的方法里的,不是外围类的一部分,所以不能访问外围类的成员变量,但是可以访问当前代码块内的外围类和常量。
使用局部和匿名内部类的区别,需要构造器和返回不止一个对象。

12.内部类的标识符
每个类都会生成class文件。
如A内有内部类B,那么A的class文件有
A.class
A$1.class 匿名类
A$B.class 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值