八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第54部分

Java编程思想第4版[中文版](PDF格式)-第54部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!





…………………………………………………………Page 183……………………………………………………………

(2) 在方法的一个作用域内定义的类  

(3) 一个匿名类,用于实现一个接口  

(4) 一个匿名类,用于扩展拥有非默认构建器的一个类  

(5) 一个匿名类,用于执行字段初始化  

(6) 一个匿名类,通过实例初始化进行构建(匿名内部类不可拥有构建器)  

  

所有这些都在 innerscopes 包内发生。首先,来自前述代码的通用接口会在它们自己的文件里获得定义,使 

它们能在所有的例子里使用:  

  

//: Destination。java  

package c07。innerscopes;  

  

interface Destination {  

  String readLabel();  

} ///:~  

  

由于我们已认为Contents 可能是一个抽象类,所以可采取下面这种更自然的形式,就象一个接口那样:  

  

//: Contents。java  

package c07。innerscopes;  

  

interface Contents {  

  int value();  

} ///:~  

  

尽管是含有具体实施细节的一个普通类,但Wrapping 也作为它所有衍生类的一个通用“接口”使用:  

  

//: Wrapping。java  

package c07。innerscopes;  

  

public class Wrapping {  

  private int i;  

  public Wrapping(int x) { i = x; }  

  public int value() { return i; }  

} ///:~  

  

在上面的代码中,我们注意到 Wrapping 有一个要求使用自变量的构建器,这就使情况变得更加有趣了。  

第一个例子展示了如何在一个方法的作用域(而不是另一个类的作用域)中创建一个完整的类:  

  

//: Parcel4。java  

// Nesting a class within a method  

package c07。innerscopes;  

  

public class Parcel4 {  

  public Destination dest(String s) {  

    class PDestination  

        implements Destination {  

      private String label;  

      private PDestination(String whereTo) {  

        label = whereTo;  

      }  

      public String readLabel() { return label; }  



                                                                                          182 


…………………………………………………………Page 184……………………………………………………………

    }  

    return new PDestination(s);  

  }  

  public static void main(String'' args) {  

    Parcel4 p = new Parcel4();  

    Destination d = p。dest(〃Tanzania〃);  

  }  

} ///:~  

  

PDestination 类属于 dest()的一部分,而不是 Parcel4 的一部分(同时注意可为相同目录内每个类内部的一 

个内部类使用类标识符 PDestination,这样做不会发生命名的冲突)。因此,PDestination 不可从 dest() 

的外部访问。请注意在返回语句中发生的上溯造型——除了指向基础类Destination 的一个句柄之外,没有 

任何东西超出dest()的边界之外。当然,不能由于类PDestination 的名字置于 dest()内部,就认为在 

dest()返回之后 PDestination 不是一个有效的对象。  

下面这个例子展示了如何在任意作用域内嵌套一个内部类:  

  

//: Parcel5。java  

// Nesting a class within a scope  

package c07。innerscopes;  

  

public class Parcel5 {  

  private void internalTracking(boolean b) {  

    if(b) {  

      class TrackingSlip {  

        private String id;  

        TrackingSlip(String s) {  

          id = s;  

        }  

        String getSlip() { return id; }  

      }  

      TrackingSlip ts = new TrackingSlip(〃slip〃);  

      String s = ts。getSlip();  

    }  

    // Can't use it here! Out of scope:  

    //! TrackingSlip ts = new TrackingSlip(〃x〃);  

  }  

  public void track() { internalTracking(true); }  

  public static void main(String'' args) {  

    Parcel5 p = new Parcel5();  

    p。track();  

  }  

} ///:~  

  

TrackingSlip 类嵌套于一个 if语句的作用域内。这并不意味着类是有条件创建的——它会随同其他所有东 

西得到编译。然而,在定义它的那个作用域之外,它是不可使用的。除这些以外,它看起来和一个普通类并 

没有什么区别。  

下面这个例子看起来有些奇怪:  

  

//: Parcel6。java  

// A method that returns an anonymous inner class  

package c07。innerscopes;  

  



                                                                                          183 


…………………………………………………………Page 185……………………………………………………………

public class Parcel6 {  

  public Contents cont() {  

    return new Contents() {  

      private int i = 11;  

      public int value() { return i; }  

    }; // Semicolon required in this case  

  }  

  public static void main(String'' args) {  

    Parcel6 p = new Parcel6();  

    Contents c = p。cont();  

  }  

} ///:~  

  

cont()方法同时合并了返回值的创建代码,以及用于表示那个返回值的类。除此以外,这个类是匿名的—— 

它没有名字。而且看起来似乎更让人摸不着头脑的是,我们准备创建一个 Contents 对象:  

return new Contents()  

但在这之后,在遇到分号之前,我们又说:“等一等,让我先在一个类定义里再耍一下花招”:  

  

return new Contents() {  

private int i = 11;  

public int value() { return i; }  

};  

  

这种奇怪的语法要表达的意思是:“创建从 Contents 衍生出来的匿名类的一个对象”。由 new 表达式返回的 

句柄会自动上溯造型成一个Contents 句柄。匿名内部类的语法其实要表达的是:  

  

class MyContents extends Contents {  

private int i = 11;  

public int value() { return i; }  

}  

return new MyContents();  

  

在匿名内部类中,Contents 是用一个默认构建器创建的。下面这段代码展示了基础类需要含有自变量的一个 

构建器时做的事情:  

  

//: Parcel7。java  

// An anonymous inner class that calls the   

// base…class constructor  

package c07。innerscopes;  

  

public class Parcel7 {  

  public Wrapping wrap(int x) {  

    // Base constructor call:  

    return new Wrapping(x) {   

      public int value() {  

        return super。value() * 47;  

      }  

    }; // Semicolon required  

  }  

  public static void main(String'' args) {  

    Parcel7 p = new Parcel7();  

    Wrapping w = p。wrap(10);  



                                                                                               184 


…………………………………………………………Page 186……………………………………………………………

  }  

} ///:~  

  

也就是说,我们将适当的自变量简单地传递给基础类构建器,在这儿表现为在“new Wrapping(x)”中传递 

x。匿名类不能拥有一个构建器,这和在调用 super()时的常规做法不同。  

在前述的两个例子中,分号并不标志着类主体的结束(和 C++不同)。相反,它标志着用于包含匿名类的那 

个表达式的结束。因此,它完全等价于在其他任何地方使用分号。  

若想对匿名内部类的一个对象进行某种形式的初始化,此时会出现什么情况呢?由于它是匿名的,没有名字 

赋给构建器,所以我们不能拥有一个构建器。然而,我们可在定义自己的字段时进行初始化:  

  

//: Parcel8。java  

// An anonymous inner class that performs   

// initialization。 A briefer version  

// of Parcel5。java。  

package c07。innerscopes;  

  

public class Parcel8 {  

  // Argument must be final to use inside   

  // anonymous inner class:  

  public Destination dest(final String dest) {  

    return new Destination() {  

      private String label = dest;  

      public String readLabel() { return label; }  

    };  

  }  

  public static void main(String'' args) {  

    Parcel8 p = new Parcel8();  

    Destination d = p。dest(〃Tanzania〃);  

  }  

} ///:~  

  

若试图定义一个匿名内部类,并想使用在匿名内部类外部定义的一个对象,则编译器要求外部对象为final 

属性。这正是我们将dest()的自变量设为final 的原因。如果忘记这样做,就会得到一条编译期出错提示。  

只要自己只是想分配一个字段,上述方法就肯定可行。但假如需要采取一些类似于构建器的行动,又应怎样 

操作呢?通过Java 1。1 的实例初始化,我们可以有效地为一个匿名内部类创建一个构建器:  

  

//: Parcel9。java  

// Using 〃instance initialization〃 to perform   

// construction on an anonymous inner class  

package c07。innerscopes;  

  

public class Parcel9 {  

  public Destination   

  dest(final String dest; final float price) {  

    return new Destination() {  

      private int cost;  

      // Instance initialization for each object:  

      {  

        cost = Math。round(price);  

        if(cost 》 100)  

          System。out。println(〃Over budget!〃);  

      }  



                                                                                          185 


…………………………………………………………Page 187……………………………………………………………

      private String label = dest;  

      public String readLabel() { return label; }  

    };  

  }  

  public static void main(String'' args) {  

    Parcel9 p = new Parcel9();  

    Destination d = p。dest(〃Tanzania〃; 101。395F);  

  }  

} ///:~  

  

在实例初始化模块中,我们可看到代码不能作为类初始化模块(即 if语句)的一部分执行。所以实际上,一 

个实例初始化模块就是一个匿名内部类的构建器。当然,它的功能是有限的;我们不能对实例初始化模块进 

行过载处理,所以只能拥有这些构建器的其中一个。  



7。6。3  链接到外部类  



迄今为止,我们见到的内部类好象仅仅是一种名字隐藏以及代码组织方案。尽管这些功能非常有用,但似乎 

并不特别引人注目。然而,我们还忽略了另一个重要的事实。创建自己的内部类时,那个类的对象同时拥有 

指向封装对象(这些对象封装或生成了内部类)的一个链接。所以它们能访问那个封装对象的成员——毋需 

取得任何资格。除此以外,内部类拥有对封装类所有元素的访问权限(注释②)。下面这个例子阐示了这个 

问题:  

  

//: Sequence。java  

// Holds a sequence of Objects  

  

interface Selector {  

  boolean end();  

  Object current();  

  void next();  

}  

  

public class Sequence {  

  private Object'' o;  

  private int next = 0;  

  public Sequence(int size) {  

    o = new Object'size';  

  }  

  public void add(Object x) {  

    if(next 《 o。length) {  

      o'next' = x;  

      next++;  

    }  

  }  

  private class SSelector implements Selector {  

    int i = 0;  

    public boolean end() {  

      return i == o。length;  

    }  

    public Object current() {  

      return o'i';  

    }  

    public void next() {  

      if(i 《 o。length) i++;  



                                                                                             186 


…………………………………………………………Page 188……………………………………………………………

    }  

  }  

  public Selector getSelector() {  

    return new SSelector();  

  }  

  public static void main(String'' args) {  

    Sequence s = new Sequence(10);  

    for(int i = 0; i 《 10; i++)  

      s。add(Integer。toString(i));  

    Selector sl = s。getSelector();      

返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的