|
|
|
|
移动端

3.4.2 迭代器、Java嵌套类和内部类

《数据结构与算法分析:Java语言描述(原书第3版)》第3章表、栈和队列,本章讨论最简单和最基本的三种数据结构。实际上, 每一个有意义的程序都将显式地至少使用一种这样的数据结构, 而栈则在程序中总是要被间接地用到, 不管我们在程序中是否做了声明。本节为大家介绍迭代器、Java嵌套类和内部类。

作者:冯舜玺/陈越 译来源:机械工业出版社|2016-04-13 17:31

有奖调研 | 1TB硬盘等你拿 AI+区块链的发展趋势及应用调研


3.4.2 迭代器、Java嵌套类和内部类

ArrayListIterator使用一个复杂Java结构, 叫作内部类(inner class)。显然该类在MyArrayList类内部被声明, 这是被许多语言支持的特性。然而, Java中的内部类具有更微妙的性质。

为了了解内部类是如何工作的, 图3-17描绘了迭代器的思路(不过, 代码有缺欠), 使ArrayListIterator成为一个顶级类。我们只着重讨论MyArrayList的数据域、 MyArray-List中的iterator方法以及ArrayListIterator类(而不是它的remove方法)。

使用是因为theItems和size()不是ArrayListIterator类的一部分

在图3-17中, ArrayListIterator是泛型类, 它存储当前位置, 程序在next方法中试图使用当前位置作为下标访问数组元素然后将当前位置向后推进。注意, 如果arr是一个数组, 则arr[idx++]对数组使用idx, 然后向后推进idx。操作++在此处存在问题。我们这里使用的形式叫作后缀++操作(postfix++operator), 此时的++是在idx之后进行的。但在前缀++操作(prefix++operator)中, arr[++idx]先推进idx然后再使用新的idx作为数组元素的下标。图3-17中的问题在于, theItems[current++]是非法的, 因为theItems不是ArrayListIterator的一部分; 它是MyArrayList的一部分。因此程序根本没有意义。71

最简单的解决方案见图3-18, 不过它也有缺点, 但是以更微小的方式呈现。在图3-18中, 我们通过让迭代器存储MyArrayList的引用来解决在迭代器中没有数组的问题。这个引用是第二个数据域, 是通过ArrayListIterator的一个新的单参数构造器而被初始化的。既然有一个MyArrayList的引用, 那么就可以访问包含于MyArrayList中的数组域(还可得到MyArrayList的大小, 该大小在hasNext中是需要的)。

图3-18中的问题在于, theItems是MyArrayList中的私有(private)域, 而由于ArrayListIterator是一个不同的类, 因此在next方法中访问theItems是非法的。最简单的修正办法是改变theItems在MyArrayList中的可见性, 从private改成某种稍宽松的可见性(如public, 或默认的可见性, 它也被称为包可见性(package visibility))。不过, 这违反了良好的面向对象编程的基本原则, 它要求数据应尽可能地隐蔽。

图3-19显示另一种解决方案, 这种方案能够正确运行: 使ArrayListIterator为嵌套类(nested class)。当我们让ArrayListIterator为一个嵌套类时, 该类将被放入另一个类(此时就是MyArrayList)的内部, 这个类就叫作外部类(outer class)。我们必须用static来表示它是嵌套的; 若无static, 将得到一个内部类, 这有时候好, 有时候也不好。嵌套类是许多编程语言的典型的类型。72注意, 嵌套类可以被设计成private, 这很好, 因为此时该嵌套类除能够被外部类MyArrayList访问外, 其他是不可访问的。更为重要的是, 因为嵌套类被认为是外部类的一部分, 所以不存在产生不可见问题: theItems是MyArrayList类的可见成员, 因为next是MyArrayList的一部分。

既然我们有了嵌套类, 那么就可以讨论内部类。嵌套类的问题在于,   在我们的原始设计中, 当编写theItems而不引用其所在的MyArrayList的时候, 代码看起来还可以, 也似乎有意义, 但却是无效的, 因为编译器不可能计算出哪个MyArrayList在被引用。要是我们自己不必明了这一点那就好多了, 而这恰是内部类要求我们所要做的。

当声明一个内部类时, 编译器则添加对外部类对象的一个隐式引用, 该对象引起内部类对象的构造。如果外部类的名字是Outer, 则隐式引用就是Outer.this。因此, 如果ArrayListIterator是作为一个内部类被声明且没有注明static, 那么MyArrayList. this和theList就都会引用同一个MyArrayList。这样, theList就是多余的, 并可能被删除。

在每一个内部类的对象都恰好与外部类对象的一个实例相关联的情况下, 内部类是有用的。在这种情况下, 内部类的对象在没有外部类对象与其关联时是永远不可能存在的。对于MyArrayList及其迭代器的情形, 图3-20指出了MyArrayList类和迭代器之间的关系, 此时这些内部类都用来实现该迭代器。

theList.theItems的使用可以由MyArray-List.this.theItems代替。这很难说是一种改进, 但进一步的简化还是可能的。正如this.data可以简写为data一样(假设不存在引起冲突的也叫作data的另外的变量), MyArrayList.this.theItems可以简写为theItems。图3-21指出ArrayListIterator的简化。74

其次, theList没有了, 我们用size()和theItems[current++]作为MyArray-List.this.size()和MyArrayList.this.theItems[current++]的简记符。theList作为数据成员, 它的去除也删除了相关的构造器, 因此程序又转变成1号版本的样式。

我们可以通过调用MyArrayList的remove来实现迭代器的remove方法。由于迭代器的remove可能与MyArrayList的remove冲突, 因此我们必须使用MyArrayList.this.remove。注意, 在该项被删除之后, 一些元素需要移动, 因此current被视为同一元素也必须移动。于是, 我们使用--而不是-1。

内部类为Java程序员带来句法上的便利。它们不需要编写任何Java代码, 但是它们在语言中的出现使Java程序员以自然的方式(如1号版本那样)编写程序, 而编译器则编写使内部类对象和外部类对象相关联所需要的附加代码。

喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:342347198

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

超级网管员——网络服务

本书全面介绍了Windows Server 2003 R2中最常用的各种服务,包括域名服务、动态IP地址服务、Windows名称服务、活动目录服务、Web服务、FTP...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊