Reputation: 971
This is a simple generic method,and passing the values the args in for loop is causing an error saying:
for-loop range must have and iterator() method
fun main(args: Array<String>) {
val arr: IntArray = intArrayOf(1,2,3,4)
val charA: CharArray = charArrayOf('a','b','c','d')
printMe(arr)
printMe(charA)
}
fun <T>printMe(args: T){
for (items in args){
println(items)
}
}
how do i make it iterate throught the values for both char[]
and array
Upvotes: 3
Views: 1953
Reputation: 23125
for-loop in Kotlin works by convention, looking statically for an operator member named iterator
which must return something that can be iterated, i.e. something that has in turn operator members next
and hasNext
.
operator
modifier on such members is required to specify that the member is to satisfy some convention, namely the iteration convention.
Since args
is of type T
and there is no iterator
member in every possible type T
, it can't be iterated readily.
However you can provide an additional parameter to printMe
, which knows how to get an iterator out of an instance of T
, and then use it to obtain an iterator and iterate it:
fun main(args: Array<String>) {
val arr: IntArray = intArrayOf(1,2,3,4)
val charA: CharArray = charArrayOf('a','b','c','d')
printMe(arr, IntArray::iterator)
printMe(charA, CharArray::iterator)
}
fun <T> printMe(args: T, iterator: T.() -> Iterator<*>) {
for (item in args.iterator()) {
println(item)
}
}
Here T.() -> Iterator<*>
is a type which denotes a function with receiver. Instances of that type can be invoked on T
as if they were its extensions.
This snippet works because the iterator returned has itself an operator extension function Iterator<T>.iterator() = this
which just returns that iterator, thus allowing to loop through the iterator with a for-loop.
Upvotes: 5
Reputation: 73490
This is actually a little bit subtle.
The key problem is that variable arr
is of type IntArray
and IntArray
is not derived from Array
. Similarly while IntArray
has an iterator()
function it does not implement Iterable<>
.
The same occurs for for CharArray
variable.
In fact IntArray
and CharArray
and Array<T>
don't seem to have any common base class or interface other than Any
. So you're either stuck passing an object and doing a typecheck in printMe
, or using overloading.
A type-checking version would look like
printMe(args:Any) {
if(args is IntArray) {
for(item in args) {
println(item)
}
} else if (args is CharArray) {
for(item in args) {
println(item)
}
} else {
println("Not an IntArray or a CharArray")
}
}
Overloading would look like this
printMe(args:IntArray) {
for(item in args) {
println(item)
}
}
printMe(args:CharArray) {
for(item in args) {
println(item)
}
}
IMO overloading is the better option since you can't end up passing an object you cant handle by mistake.
Upvotes: 2
Reputation: 24324
The problem is that the compiler does not know you are passing an Array
and T
can be of any type.
One way to fix this would be using is
operator:
fun <T>printMe(args: T){
if(args is Array<*>) {
for (items in args) {
println(items)
}
}
}
Upvotes: 0