26 March 2014

最近一个多月都在做一个Android应用(比赛),昨天下午提交了代码和文档,昨晚完成了视频的制作(第一次做视频)。后面还要做一些演示和答辩,不过这些都不涉及编码,终于松了一口气。

继续写java这个系列,内部类看了两遍,在此权当复习一下。由于内部类接触不多,这里要举不少书中代码。


内部类顾名思义,就是在一个类中定义另一个类。至少从表面上看来,它有一些有用的地方,比如对代码的整合,另外,可以通过内部类中的元素的可视性来实现某些目的。那先举一个简单的内部类的例子:

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

当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能够访问其外围对象的所有成员,而不需要任何特殊条件。实际上,外围类创建了一个内部对象时,此内部对象必定会秘密地捕获一个指向那个外围类的对象的引用,不过不必担心,这些都是有编译器来处理的。

如果你需要生产对外部对象的引用,可以使用外部类的名字后面紧跟圆点和this:

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

如果你想告知某些其他对象,去创建某个内部类的对象,则:

public class DotNew {
  public class Inner {}
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
     dti.outer().f();
  }
}

你不必也不能声明为dn.new DotNew.Inner()

还有一种称为嵌套类的方式,即静态内部类,它不需要对外部类对象的引用(记得之前的那些静态类么,不需要实例化就可以使用)。通常,不能在接口内部放置代码,但嵌套类可以作为接口的一部分。

private内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。此外,从客户端程序员的角度来看,由于不能访问任何新增加的,原本不属于公共接口的方法,所以扩展接口是没有价值的。书中的这段话比较抽象,我们来看看下面这个例子:

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

我们发现,PContents是私有类型的类,外部不能够访问,而内部类PDestination是protected型,也只有Parcel4和其子类才能访问。这样处理,使得客户端程序员来了解或访问这些成员,是要受限的。

接下来要说一个重要的概念——匿名内部类。如果你在阅读别人代码的过程中遇到了一些奇怪的用法(Android中有很多),别着急,那非常可能是匿名内部类。看看下面的例子:

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

初看,似乎定义了一个叫做contents的方法,它返回一个Contents对象。但却在返回后插入了一个类的定义(注意分号)。实际上,是创建一个继承自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();
  }
} 

实际上,匿名类将MyContents类的定义,与一个返回该类对象的方法合并到了一起。这时,MyContents由于是继承Contents类(接口),在匿名类中,直接向上转型为Contents的引用。

如果匿名类想要使用一个在其外部定义的对象,那么参数应该是final型:

public class Parcel9 {
  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");
  }
}

如果希望为匿名内部类创建一个构造器的效果,则:

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()");
      }
    };
  }
  public static void main(String[] args) {
    Base base = getBase(47);
    base.f();
  }
}

这里不要求变量i一定是final的,因为i被传递给匿名类的基类的构造器,而并不会被匿名类内部被直接使用。



blog comments powered by Disqus