for .. in 은 무엇일까

DoDoBest

·

2024. 3. 8. 21:32

학습하게 된 계기

코틀린 코루틴 책을 학습하다가 for문에 Channel class를 사용하는 코드를 보고, list도 아닌 class가 어떻게 원소를 반환하는지 궁금해졌다.

suspend fun main(): Unit = coroutineScope {
    val channel = Channel<Int>()

    launch {
        repeat(1) { index ->
            println("Producing next one")
            delay(1000)
            channel.send(index * 2)
        }
        channel.close()
    }

    launch {
        for (element in channel) {
            println(element)
        }
    }
}

 

for … in 의 의미

아래와 같은 코드에서 in은 list.iterator()를 호출해서 이터레이터를 얻은 다음, 자바와 마찬가지로 그 이터레이터에 대해 hasNext와 next 호출을 반복하는 식으로 변환된다.

fun main() {
    val list = listOf(1,2,3)
    for (i in list) {
        println(i)
    }
}

 

자바로 디컴파일 된 코드는 다음과 같다. for .. in 문이 while문으로 바뀐 것을 볼 수 있다.

 

public final class MainKt {
   public static final void main() {
      List list = CollectionsKt.listOf(new Integer[]{1, 2, 3});
      Iterator var2 = list.iterator();

      while(var2.hasNext()) {
         int i = ((Number)var2.next()).intValue();
         System.out.println(i);
      }

   }
   ...
}

 

 

이것을 통해 Class가 iterator를 반환한다면 클래스 자체를 for .. in 다음에 놓을 수 있다는 것을 알게 되었다.

 

Iterator는 무엇인가?

Kotlin 공식 문서에서 Iterator를 아래와 같이 소개하고 있다.

For traversing collection elements, the Kotlin standard library supports the commonly used mechanism of iterators – objects that provide access to the elements sequentially without exposing the underlying structure of the collection
https://kotlinlang.org/docs/iterators.html

 

Iterator는 collection의 내부 구현을 노출하지 않고 collection 원소들에 순차적으로 접근할 수 있도록 도와주는 객체다.

 

MutableList의 상속 관계를 살펴보면, List와 MutableCollection 인터페이스를 상속하고 있고, 이 둘은 Collection 인터페이스를 상속하며, Collection은 Iterable 인터페이스를 상속한다.

 

Iterable 인터페이스에 Iterator를 반환하는 함수가 있으며 Iterator는 next, hasNext 함수를 가지고 있는 인터페이스다!

Iterator를 제공하는 클래스는 어떻게 만들 수 있을까?

 

따라서 iterator 인터페이스를 상속하거나, iterable 인터페이스를 구현하고 iterator 구현체를 반환하는 iterator operator 함수를 클래스에 생성하면 된다.

 

class Progression: Iterator<Int> {

    private var progressStatus = 0
    private val maxStatus = 100

    override fun hasNext(): Boolean = progressStatus <= maxStatus

    override fun next(): Int {
        if (progressStatus > maxStatus) throw IllegalStateException()
        return progressStatus++
    }

}


fun main() {
    val progression = Progression()
    for (i in progression) {
        println(i)
    }
}