Java编程思想--10内部类

第十章 内部类

  • 10.1 创建内部类
  • 10.2 链接到外部类
  • 10.3 使用.this与.new
    • 10.3.1 生成对外部对象的引用
    • 10.3.2 创建内部类对象
  • 10.4 内部类与向上转型
  • 10.5 在方法和作用域的内部类
    • 10.5.1 局部内部类
  • 10.6 匿名内部类
    • 10.6.1 匿名类的使用
    • 10.6.2 匿名类中的初始化
    • 10.6.3 再访工厂方法
  • 10.7 嵌套类
    • 10.7.1接口的内部类
    • 10.7.2 从多重嵌套类中访问外部类成员
  • 10.8 为什需要内部类
      • 类解决了多重继承的问题
      • 使用内部类的其他特性
    • 10.8.1 闭包与回调
    • 10.8.2 内部类与控制框架
  • 10.9 内部类的继承
    • 10.0 内部类会被覆盖吗
  • 10.11 局部内部类--对比匿名内部类
  • 10.12 内部类与标识符
  • 10.13 总结

讲一个类的定义放在另一个类定义的内部,这就是内部类。

10.1 创建内部类

在类内定义类

10.2 链接到外部类

内部类对象拥有外部类所有元素的访问权,不需要任何条件。应用:“迭代器”设计模式。

//: innerclasses/Sequence.java
// Holds a sequence of Objects.

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 {
      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 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

10.3.1 生成对外部对象的引用

使用.this

//: innerclasses/DotThis.java
// Qualifying access to the outer-class object.

public class DotThis {
  void f() { System.out.println("DotThis.f()"); }
  public class Inner {
    public DotThis outer() {
      return DotThis.this;
      // A plain "this" would be Inner's "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();
  }
} /* Output:
DotThis.f()
*///:~

10.3.2 创建内部类对象

无法直接创建,需要先创建外部类,然后再调用.new

//: innerclasses/DotNew.java
// Creating an inner class directly using the .new syntax.

public class DotNew {
  public class Inner {}
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
  }
} ///:~

10.4 内部类与向上转型

将内部类上转型为其基类,尤其是转型成一个接口时,内部类就有了用武之地。这是因为此内部类–某个接口的实现–能够完全不可见。

//: innerclasses/Destination.java
public interface Destination {
  String readLabel();
} ///:~
//: innerclasses/Contents.java
public interface Contents {
  int value();
} ///:~
//: innerclasses/TestParcel.java

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");
    // Illegal -- can't access private class:
    //! Parcel4.PContents pc = p.new PContents();
  }
} ///:~

可以看到,将内部类设置为private(或者protected)后,只能有外部类的方法才能访问得到。无法通过内部类来创建内部类对象。private内部类给设计者提供一种路径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏实现的细节。此外从客户端看,由于无法访问任何新增加的,原本不属于接口的公共接口方法,所以拓展接口是没有价值的。

10.5 在方法和作用域的内部类

使用内部类的场景:

  1. 实现某个类型的接口,可以创建并返回对其的引用。
  2. 解决一个复杂的问题,需要创建一个类来加以解决,但又不想这个类公共可用。

10.5.1 局部内部类

可以在任何作用域内创建局部类,并且可以在任意类的某个内部类中取相同的名字,并不会发生命名冲突。

10.6 匿名内部类

10.6.1 匿名类的使用

如下例:创建了一个实现Contents接口的匿名类。在返回值时,发生了向上转型。

public class Parcel7 {
    public Contents contents() {
      return new Contents() { // Insert a class definition
        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();
    }
} ///:~

10.6.2 匿名类中的初始化

如果实现的接口/基类需要参数,那么只需要给相应的构造器传参数即可。
在匿名类定义字段时,还能够对其初始化,但是将参数引用设置成final
匿名类没有构造器,但是可以通过实力初始化达到构造器的效果。

// /: innerclasses/Parcel9.java
// An anonymous inner class that performs
// initialization. A briefer version of Parcel5.java.

public class Parcel9 {
  // Argument must be final to use inside
  // anonymous inner class:
  public Destination destination(String dest, int i, Object ob) {
    return new Destination() {
      private String label = dest;
      private int lv = i;
      private Object obj = ob;
      public String readLabel() { return label; }
    };
  }
  public static void main(String[] args) {
    Parcel9 p = new Parcel9();
    Destination d = p.destination("Tasmania", 10, new Object());
  }
} ///:~

10.6.3 再访工厂方法

匿名类使工厂方法变得美妙。因为可以隐式的在工厂方法中定义匿名类并返回。

10.7 嵌套类

声明为static的内部类通常被称为内部类。

  1. 要创建嵌套类,并不需要其外部对象。
  2. 不能从嵌套类中访问非静态的外部变量。
  3. 在普通内部类中,通过特殊关键字this可以访问到外围对象,但静态内部类没有这一关键字。

10.7.1接口的内部类

正常情况下,不能再接口里面放置任何代码,但是嵌套类可以作为接口的一部分。放在接口中的任何类都是public与static的。因为类是static的所以,将嵌套类至于接口中并不违反规则。如果想创建公共代码,使得他们可以被某个接口的所有不同实现所公用,那么在接口中嵌套内部类会显得很方便。

//: innerclasses/ClassInInterface.java
// {main: ClassInInterface$Test}

public interface ClassInInterface {
  void howdy();
  class Test implements ClassInInterface {
    public void howdy() {
      System.out.println("Howdy!");
    }
    public static void main(String[] args) {
      new Test().howdy();
    }
  }
} /* Output:
Howdy!
*///:~

10.7.2 从多重嵌套类中访问外部类成员

一个内部类被嵌套所少层并不重要——它能透明地访问所有它所嵌入的外围类的所有成员。

10.8 为什需要内部类

内部类最吸引人的原因:每个类内部都能独立地继承自一个(接口的)实现,所以无论外围类时都已经继承了某个(接口的)实现。对于内部类都是没有影响的。


内部类还可以无障碍的访问外部类成员。

类解决了多重继承的问题

当类实现了两个接口时,可以使用继承或者内部类。但是当拥有的是抽象或者具体的类时(不是接口),此时只能继承一个类,那就只能使用内部类才能实现多重继承。

集成多个接口

//: innerclasses/MultiInterfaces.java
// Two ways that a class can implement multiple interfaces.
package innerclasses;

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
  B makeB() {
    // Anonymous inner class:
    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());
  }
} ///:~

拥有多个抽象的或者具体的类

//: innerclasses/MultiImplementation.java
// With concrete or abstract classes, inner
// classes are the only way to produce the effect
// of "multiple implementation inheritance."
package innerclasses;

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”关系;它就是一个独立的实体。

10.8.1 闭包与回调

闭包:是一个可调用的对象,它记录了一些信息,这些信息来自创建它的作用域。
内部类可以看作是类的闭包。他不仅包含万为对象的信息,还自动包含一个指向外围类对象的引用,在作用域内,内部类有权操作所有成员,包括private成员。

人们认为Java应该包含某种类似的机制,以允许回调。通过糊掉,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。Java因指针安全考虑,没有在语言里包含指针。

10.8.2 内部类与控制框架

内部类使得控制框架变得方便:
(1)控制框架的完整实现是由单个类创建,从而使的实现的细节被封装起来。内部类用来表示解决问题所需的各种不同的action()。
(2)内部类能够很容易地访问外围的任意成员,所以可以避免这种实现变得笨拙。

10.9 内部类的继承

暂pass。

//: innerclasses/InheritInner.java
// Inheriting an inner class.

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);
    }
  } ///:~

10.0 内部类会被覆盖吗

下例中,如果发生覆盖,则第二行应该输出的是覆盖后的Yolk版本。当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化,这两个内部类完全是不同的个体,各自在自己的命名空间里。如果明确继承某个内部类是可以的。

class Egg {
  private Yolk y;
  protected class Yolk {
    public Yolk() { System.out.println("Egg.Yolk()"); }
  }
  public Egg() {
    System.out.println("New Egg()");
    y = new Yolk();
  }
}	

public class BigEgg extends Egg {
  public class Yolk {
    public Yolk() { System.out.println("BigEgg.Yolk()"); }
  }
  public static void main(String[] args) {
    new BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~
//: innerclasses/BigEgg2.java
// Proper inheritance of an inner class.
import static net.mindview.util.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:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*///:~

10.11 局部内部类–对比匿名内部类

局部内部类对比匿名内部类的区别在于:需要一个已命名的构造器,或者需要重载构造器,而匿名类只能用于实例初始化。

10.12 内部类与标识符

普通的类在编译后会产生.class文件。而内部类与匿名类也会产生,并且有严格的命名规则:

  1. 内部类:[外部类名]$[内部类名].class
  2. 匿名类:[外部类名]$[数字].class
  3. 嵌套:加载其外围标识符与"$"后面

10.13 总结

Java中接口和内部类结合,解决多继承的问题。

你可能感兴趣的:(Java基础复习)