Java 版 (精华区)

发信人: angle (finder), 信区: Java
标  题: 内 嵌 类 简 介(3)
发信站: 哈工大紫丁香 (Mon Jun 21 12:04:01 1999), 转信

 3 一 种 内 嵌 类: 匿 名 类

          当 你 只 是 想 传 递 一 个 做 些 事 情 的 方 法 时 你
 甚 至 不 需 名 (类 似 于C 语言 的 回 调 函 数), 你 只 需 建 立
 一 个 匿 名 的 内 嵌 类。 这 些 类 只 是有 具 体 名字 的 内 嵌 
类。 只 用 一 次 的 类 一 般 不 命 名。

  为 说 明 匿 名 类, 我 们 编 写 了 BinarySearchTree 的 keys 
和 elements 方 法,它们都用来返回一个BSTEnumerator 对象, 如下所
 示。

     private Enumeration doEnumerate(final boolean k) {
         return new Enumeration() {
             private BSTNode currentNode = rootNode;
             private boolean keys = k;
           public boolean hasMoreElements() {
                 return (currentNode != null);
             }
             public Object nextElement() {
                 if (currentNode == null)
                     throw new NoSuchElementException();
                 BSTNode n = currentNode;
                 currentNode = n.successor();
                 return (keys ? n.key : n.payload);
             }
         };
     }

     public Enumeration elements() {
         return doEnumerate(false);
     }

     public Enumeration keys() {
         return doEnumerate(true);
     }

    仔 细 看 一 下doEnumerate 函 数; 有 两 样 事 情 比 较 奇 怪。
 第 一,它 返 回 接口 的 新 的 实 例; 
其 次, 它 前 面 的 参 数k 是final 型 的。 到 底 是 怎 么回事 呢?
 答案 是 这 种 技 术 是 使 用 内 嵌 类 的 又 一 方 法。

     返 回 说 明 看 起 来 是 建 立 了 一 个 新 的 接 口 实 例,
 但 事 实 上是 建 立 了 一个 没 有 明 确 名 字 并 且 实 现 了 接 
口 的 新 类。 在 这 种 情 况 下, 实 际上 的 名 字是BinarySearchTree$1,
 但 它 只 对 编 译 器 的 作 者 有 意 义。
     用 这 种 方 法 建 立 类 的 一 个 局 限 是, 新 的 类 不 能 
定 义 构 造操 作( 记 住:它 没 有 名 字), 因 此 在 类 中 必 须
 有 办 法 设 置 初 始 状 态。JAVA1.1 的办 法 是 重新 定 义 对 非
 类 变 量 的 操 作。 尤 其 是 现 在 能 合 法 地 把 它 们 象 静 态
变 量 一 样初 始 化, 却 不 用 把 它 们 放 在 静 态 初 始 化 块 
中。 这 对 程 序 员 来说意 味 着 你能 说 明 非 静 态 的 并 且 预 
先 初 始 化 的 实 例 的 值, 例 如“int foo=10;”。

   当 然, 当 你 创 建 内 嵌 类 时, 一 般 你 喜 欢 从 包 含 
在enclosing类 的 信 息 中初 始 化 内 嵌 类。( 在 例 子 中,
BinarySearchTree 就 是 包 容 类。) 这 是 通过 这 样一 些 规 则
 完 成 的: 如 果 说 明 在 定 义 类 的 地 方, 那 么 内 嵌 类 能 
访问 所 说 明所 有 的 字 段( 变 量)。 这 样 在 上 例 中, 初 始
 化“currentNode=rootNode”是 指包 容 类 变 量 中 的 实 例 变 量
rootNode。

   在 上 面 的 匿 名 类 中 有 第 二 个 初 始 化:“keys=k;”。
C 程 序 员看 到 它 立 即会 警 觉 告:“ 你 在 使 用 返 回 值 堆 栈
 中 的 值 !” 注 意,k 在 这 段 代码中 是 正式 与doEnumerate 相
 关 的 参 数。 这 就 是“final” 关 键 字 新 语 义 的 原 因
所 在。

   在JAVA 语 言 中, 变 量 的final 属 性 的 含 义 是, 它 的 值
 只 能 分配 一 次。 因此, 一 旦 分 配 了 后,final 变 量 的 值 
就 不 能 再 变。 在 上 面 的 例 子 中编 译 器 可以 安 全 地 把k 
的 值 拷 贝 到 固 定 的 存 储 区 中。

   JAVA 是“ 值 调 用” 语 言, 意 思 是 方 法 的 所 有 参 数 都
 有 值,值 的 改 变 不会 影 响 该 值 的 调 用 方 法 拷 贝。 如 果
 将 对 象 引 用 也 作 为 值 来 对 待, 这 种 语义 就 显 得 有 些 
模 糊, 并 且 如 果 传 递 了 对 象 的 值 后 又 修 改 了 它,
所 有 有 同样 引 用 的 类 都 会 找 到 修 改 后 的 对 象。 虽 然 
这 看 起 来 有 些 混 乱,但JAVA 的语 义 实 际 是 一 致 的。

    匿 名 内 嵌 类 有 这 样 一 个 问 题: 当k 的 值 改 变 时 随 
着k 的 分配 会 有 什 么变 化 ? 考 虑 一 下 下 面 这 段 混 乱 的 
代 码:

      private Enumeration doEnumerate(boolean k) {
         Enumeration ee = new Enumeration() {
             private BSTNode currentNode = rootNode;
             private boolean keys = k;
             public boolean hasMoreElements() { ... }
             public Object nextElement() { ... }
         };
         k = !k;
         return ee;
     }


          上 面k 的 值 在 返 回ee 之 前 被 修 改。 虽 然 现 在 
能 等 到 需 要 时再 建 立 对 象实 例, 但 如 何 确 定 使 用 了 哪
 个k 值 ? 是 执 行new 时 的k 值, 还 是 执行return 时的k 值 ?
 回 答 是 你 没 法 确 定, 因 而JAVA 软 件 工 程 师 们 规 定k 的
 值 必须 声 明为final, 编 译 器 就 会 强 制k 的 值 不 变。 当k 
的 值 被 说 明 为final 后,编译 器 就 会忽 略 改 变 其 值 的 说
 明, 决 定k 值 的 语 义 就 不 再 是 模 棱 两 可 的 了。

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: www-post@bbs.hit.edu]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:4.047毫秒