尽管 Kotlin 的“Map”不是“Iterable”,但它如何以及为什么是可迭代的?

SMM*_*MMH 4 loops iterable standard-library kotlin

与 Java 中一样,任何对象都Iterable可以在增强的 for 循环中使用。我认为 Kotlin 也同样如此,直到我发现事实kotlin.collections.Map并非如此Iterable

来自标准库源代码:

public interface Map<K, out V> {
    // ....
Run Code Online (Sandbox Code Playgroud)

如果不是,那么这样的事情怎么可能?:

val map: Map<String, Int> = HashMap()
// ...
for ((s, i) in map) {
    println(s)
    println(i)
    // works perfectly
}
Run Code Online (Sandbox Code Playgroud)

最后,是否可以制作支持此语法的自定义类?如果是这样,怎么办?

Swe*_*per 6

Kotlin for 循环是按约定定义的

Kotlin 中的某些语法形式是按约定定义的,这意味着它们的语义是通过一种语法形式的语法扩展为另一种语法形式来定义的。

在 的情况下for(VarDecl in C) Body,它扩展为的语法形式是:

when(val $iterator = C.iterator()) {
    else -> while ($iterator.hasNext()) {
                val VarDecl = $iterator.next()
                <... all the statements from Body>
            }

Run Code Online (Sandbox Code Playgroud)

规范中所指定。

不需要特定的接口,只需要重载解析为,和找到适当的operator重载。作为一个极端的例子,这可以编译!iterator()hasNext()next()

fun main() {
    for (s in Foo()) { // no error here!
        
    }
}

class Foo {
    operator fun iterator(): Bar = Bar()
}

class Bar {
    operator fun hasNext() = false
    operator fun next() = ""
}
Run Code Online (Sandbox Code Playgroud)

在你的情况下,存在一个扩展iterator函数Map

operator fun <K, V> Map<out K, V>.iterator(): Iterator<Entry<K, V>>
Run Code Online (Sandbox Code Playgroud)

MutableMap

@JvmName("mutableIterator") 
operator fun <K, V> MutableMap<K, V>.iterator(): MutableIterator<MutableEntry<K, V>>
Run Code Online (Sandbox Code Playgroud)

iterator方法不必在中声明Map- 只要重载解析可以解析它,for 循环就可以工作。这同样适用于nexthasNext

因此,要使您自己的类使用 for 循环,您至少需要声明:

operator fun iterator(): Iterator<Something>
Run Code Online (Sandbox Code Playgroud)

但我仍然建议您实现Iterable,因为这样您就可以Iterable免费获得所有扩展功能:)