Kotlin: generic method and loop for iterator request ()

This is a simple generic method, and when passing in values, the args in for loop raises an error:

the for-loop range must also have an 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 can i make it repeat the values ​​for char[]

botharray

+3


source to share


3 answers


The for-loop in Kotlin works by convention, statically stating a named member statement iterator

that should return something that can be iterated over, i.e. something that has in turn member members next

and hasNext

.

Modifier

operator

for such members, it must indicate that the member must satisfy some convention, namely an iterative convention.

Since it args

has a type T

and does not exist iterator

in all possible types T

, it cannot be easily overwritten.

However, you can provide an additional parameter printMe

that knows how to get an iterator from an instance T

, and then use it to get an iterator and iterate over 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 that denotes a function with a sink . Instances of this type can be called on T

as if they were extensions of it.

This snippet works because the returned iterator itself has a statement extension function Iterator<T>.iterator() = this

that simply returns that iterator, allowing the iterator to loop through with a while loop.

+5


source


It's a bit subtle actually.

The main problem is that the variable arr

is of type IntArray

and IntArray

not derived from Array

. Likewise, while it IntArray

has a function iterator()

, it does not implement Iterable<>

.

The same happens for a variable CharArray

.

In fact IntArray

both CharArray

and Array<T>

and do not seem to have a common base class or interface other than Any

. So you either loop over passing an object and do a typecheck in printMe

, or you use an overload.

The 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")
   }
}

      

The overload will look like this:

printMe(args:IntArray) {
  for(item in args) {
    println(item)
  }
}

printMe(args:CharArray) {
  for(item in args) {
    println(item)
  }
}

      

Overloading is IMO the best option as you cannot end up passing an object that you cannot handle by mistake.

+2


source


The problem is that the compiler doesn't know what you are passing Array

and T

can be of any type.

One way to fix this would be to use the operator is

:

fun <T>printMe(args: T){
    if(args is Array<*>) {
            for (items in args) {
                println(items)
            }
    }
}

      

0


source







All Articles