运算符重载和迭代器模式

所谓迭代器,它的核心作用就是将遍历和实现分离开来,在遍历的同时不需要暴露对象的内部表示。

迭代器(iterator)是Java中我们非常熟悉的东西了,数据结构如List和Set都内置了迭代器,我们可以用它提供的方法来顺序地访问一个聚合对象中每个元素。

有时候,我们会定义某些容器类,这些类中包含了大量相同类型的对象。如果你想给这个容器类的对象直接提供迭代的方法,如hasNext,next,first等,那么就可以自定义一个迭代器。然而通常情况下,我们不需要自己再实现一个迭代器,因为Java标准库提供了java.util.Iterator接口,你可以用容器类实现该接口,然后再实现需要的迭代器方法。

这种设计模式就是迭代器模式,它的核心作用就是将遍历和实现分离开来,在遍历的同时不需要暴露对象的内部表示。迭代器模式非常容易理解,你可能已经非常熟悉。但我们还是举个具体的例子来介绍下这种模式,接着引出Kotlin中相关的语法特性,继而进行改良。

方案1:实现Iterator接口

data class Book(val name: String)

class Bookcase(val books: list<Book>): Iterator<Book> {
private val iterator: Iterator<Book>
init {
this.iterator = books.iterator()
}
override fun hasNext() = this.iterator.hasNext()
override fun next() = this.iterator.next()
}

//invoke
val bookcase = Bookcase(listOf(Book("Dive into Kotlin"),Book("Thinking in Java")))
while(bookcase.hasNext()) {
println("The book is ${bookcase.next().name}")
}

The book name is Dive into Kotlin
The book name is Thinking in Java

由于Bookcase对象拥有与List实例相同的迭代器,我们就可以直接调用后者迭代器所有的方法。一种更简洁的遍历打印方式如下:

for(book in bookcase) {

println("The book name is ${book.name}")
}

方案2:重载iterator方法

Kotlin却有更好的解决方案。Kotlin有一个非常强大的语言特性,那就是利用operator关键字内置了很多运算符重载功能。我们就可以通过重载Bookcase类的iterator方法,实现一种语法上更加精简的版本:

data class Book(val name: String)

class Bookcase(val books:List<Book>) {
operator fun iterator(): Iterator<Book> = this.books.iterator()
}

这样我们用一行代码就实现了以上所有效果。由于Kotlin支持扩展函数,这意味着我们可以给所有的对象都内置一个迭代器。

方案3:通过扩展函数

假设现在的Book是引入的一个类,你并不能修改它的源码,那么如何用扩展的语法来给Bookcase类对象增加迭代的功能:

data class Book(val name: String) {}

class Bookcase(val Books: list<Book>) {}

operator fun Bookcase.iterator(): Iterator<Book> = books.iterator()

代码依旧非常简洁,假如想对迭代器的逻辑有更多的控制权,那么也可以通过object表达式来实现:

operator fun Bookcase.iterator():Iterator<Book> = object: Iterator<Book> {

val iterator = books.iterator()
override fun hasNext() = iterator.hasNext()
override fun next() = iterator.next()
}

总的来说,迭代器模式并不是一种很常用的设计模式,但通过它我们可以进一步了解Kotlin中的扩展函数的应用,以及运算符重载功能的强大之处。

作者

8MilesRD

发布于

2020-01-19

更新于

2020-01-19

许可协议

评论