본문 바로가기

Dev Book/Kotlin IN ACTION

CH7(7.3). 컬렉션과 범위에 대한 관례

1. 인덱스로 원소에 접근: get과 set

- 인덱스 연산자도 관례를 따른다. 인덱스 연산자를 사용해 원소를 읽는 연산은 get 연산자 메소드로 변환되고, 원소를 쓰는 연산은 set 연산자 메소드로 변환된다. 

 

operator fun Point.get(index: Int): Int{
    return when(index){
        0 -> x
        1 -> y //주어진 인덱스에 해당하는 좌표를 찾는다.
        else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

>>>val p = Point(10, 20)
>>>println(p[1])
20

 

data class MutablePoint(var x:Int, var y:Int)
operator fun MutablePoint.set(index:Int, value:Int){
    when(index){
        0 -> x = value //주어진 인덱스에 해당하는 좌표 변경
        1 -> y = value
        else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

>>>val p2 = MutablePoint(10, 20)
>>>p2[1] = 42
>>>println(p2)
MutablePoint(x=10, y=42)

 

2. in 관례

- in은 객체가 컬렉션에 들어가있는지 검사한다. in 연산자와 대응하는 함수는 contains다.

 

data class Rectangle(val upperLeft: Point, val lowerRight: Point)

operator fun Rectangle.contains(p:Point):Boolean{
    return p.x in upperLeft.x until lowerRight.x && 
            p.y in upperLeft.y until lowerRight.y
            
}

>>>val rect = Rectangle(Point(10, 20), Point(50, 50))
>>>println(Point(20,30) in rect)
true

 

3. rangeTo 관례

- .. 연산자는 rangeTo 함수를 간략하게 표현하는 방법이다. rangeTo 함수는 범위를 반환한다.

 

>>>val now = LocalDate.now()
>>>val vacation = now..now.plusDays(10) //10일짜리 범위
>>>println(now.plusWeeks(1) in vacation)
true

 

now..now.plusDays(10)은 컴파일러에 의해 now.rangeTo(now.plusDays(10))으로 변환된다. 이때 rangeTo 함수는 Comparable에 대한 확장함수이다. 

 

operator fun <T: Comparable<T>> T.rangeTo(that:T): ClosedRange<T>

4. for 루프를 위한 iterator 관례

- for 루프는 범위 검사와 똑같이 in 연산자를 사용하지만 이 경우 쓰이는 in의 의미는 다르다. for(x in list)의 경우 list.iterator()를 호출한 후 이터레이터에 대한 hasNext와 next 호출을 반복하는 식으로 변환된다.

 

operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =
    object:Iterator<LocalDate>{
        var current = start
        override fun hasNext() = current <= endInclusive //compareTo 관례를 사용해 날짜를 비교
        override fun next() = current.apply{
            current = plusDays(1) //현재 날짜를 1일 뒤로 변경
        }
    }
    
>>>val newYear = LocalDate.ofYearDay(2022, 1)
>>>val daysOff = newYear.minusDays(1)..newYear //LocalDate 범위 객체 -> ClosedRange<LocalDate> 반환
>>>for(dayOff in daysOff){
...        println(dayOff)
...    }
2021-12-31
2022-01-01

 

rangeTo 함수는 ClosedRange의 인스턴스를 반환한다. 따라서 코드에서 ClosedRange<LocalDate>에 대한 확장 함수 iterator를 정의했기 때문에 LocalDate의 범위 객체를 for 루프에 사용할 수 있다.