As mentioned before, for iterates through anything that provides an iterator, i.e.
has a member- or extension-function iterator() , whose return type
has a member- or extension-function next() , and
has a member- or extension-function hasNext() that returns Boolean .
All of these three functions need to be marked as operator .
abstract operator fun iterator(): Iterator<T>
abstract operator fun hasNext(): Boolean
abstract operator fun next(): T
I found that they must be operator fun and I tried to understand operators in Kotlin, but I canât understand why. Is there any document how and when theyâre interpreted as operator?
I donât know, but maybe theyâre treated as operator because theyâre called without explicit function call statement, but itâs not easy to find about these things in documents.
Yes! The key point is that there is a special syntax that will make use of these functions without a syntactically visible call. Without marking them as operator, the special syntax would not be available.
Thanks for the explanation.
Iâm not familiar with language design theory, so I donât know if this is common philosophy of operators, but it would be better if the language document explicitly explains it because it explains operator overloadings and operator-thing in iterator which made me think itâs not explaining operator keyword fully.
There is only a small set of function names that can be marked as operators in Kotlin. Once you have learned them, thatâs it. Repeating how they work in the documentation of every instance of an operator function would be superfluous.
I understand. But I was curious about why they are operator funs (I mean how they are interpreted and works in common way), rather than listing all or each of them.
The reason why they need to be flagged as operator is probably just to make them more explicit. I donât think there is a theoretical necessity to mark such functions as operators. Theoretically it could work as soon as the function name and signature matches the operator. In fact, afair methods defined in Java implicitly are treated as operators when their signatures match.
At first, I think marking with âoperatorâ can avoid the calling which is treated as operators coincidently but you do not want , when the name of function is just same as syntax, so it need to be flagged explicitly with âoperatorâ
Secondly, you can declare the operator function with extention function , rather than all in the class body; At this
time you can know that it is operator explicityly.
Additionly, the result of function âiteratorâ do not need bo be inhertor of interface âIteratorâ, and it could work as soon as the result has two function which matches the operator . you can see the demo below:
class A {
private var i = 0
operator fun hasNext() = i < 10
operator fun next() = i++
}
operator fun A.iterator() = this
fun foo(){
for(i in A()) print(i)
}
In fact, It also used to work that way for Kotlin functions. That was changed back in 2015:
This has been debated a lot in the past, and we finally decided that we want to introduce some more discipline into how operator overloading and infix function calls work in Kotlin. At the moment any function named plus that can be called as a.plus(b) can also be called as a + b . We will require such functions to be marked with the operator modifier, otherwise the operator notation will not be available for them. This makes operator use more disciplined and eliminates the possibility of random punctuation creeping into APIs. The most common example would be having a method called get but totally not intending it for use as square brackets.
In fact I donât understand the interest to not explicitly implements Iterator and/or Iterable when you create your own classes. It more clear that the capacity to iterate is present without check all code to see if there those operator or not.
There also a draw back, only for(t in a) will work, canât use as Iterator or Iterable, so loose some functionalities and extensions
The only interest that I can see is if use an external library and need add these behavior with extensions.
You can also avoid boxing by not implementing Iterable/Iterator. IMO itâs nice that the type system isnât needed to âtalk to the compilerâ like this.
object SquareNumbers {
operator fun iterator() = SquareNumbersIterator()
class SquareNumbersIterator {
private var i = 0
operator fun hasNext(): Boolean = true
operator fun next(): Int = (i * i).also { i++ }
}
}
for (n in SquareNumbers) {
if (n > 100) break
println(n)
}
This also reminds me of an interesting C# proposal about allowing using (i.e. the try-with-resources of C#) to work with types that have Dispose() functions without actually implementing IDisposable (which is what using requires). Basically wanting to treat Dispose() as a Kotlin operator function and decouple using from the type system.