컬렉션과 범위에 대해 쓸 수 있는 관례

유우선·2026년 2월 16일

Kotlin Study📚

목록 보기
23/32
  • 컬렉션에서 가장 자주 쓰는 연산
    1. 인덱스로 원소를 읽거나 쓰기
    2. 어떤 값이 컬렉션에 속해 있는지 검사
    • 이 연산들은 연산자 구문으로 사용할 수 있음
  • 인덱스 접근 → collection[index]
  • 원소가 컬렉션이나 범위에 속해 잇는지 검사 → in 연산자

인덱스로 원소에 접근 (get / set)

  • 코틀린의 인덱스 접근 연산자 → 관례를 따름
    • 원소를 읽는 연산 → get 연산자 메서드
    • 원소를 쓰는 연산 → set 연산자 메서드

get 메서드

class Point(val x: Int, val y: Int)

operator fun Point.get(index: Int) : Int {
    return when(index) {
        0 -> x
        1 -> y
        else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

fun main() {
    val p = Point(10, 20)
    println(p[1])
    // 20
}
  • get 메서드의 파라미터 → Int가 아니어도 됨
    • 컬렉션 클래스가 다양한 키 타입을 지원해야 한다면 파라미터 타입에 대해 오버로딩한 get 메서드를 여럿 정의할 수 있음

set 메서드

data class MutablePoint(var x: Int, var y: Int)

operator fun MutablePoint.set(index: Int, value: Int) {
    return when(index) {
        0 -> x = value
        1 -> y = value
        else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

operator fun MutablePoint.get(index: Int) : Int {
    return when(index) {
        0 -> x
        1 -> y
        else -> throw IndexOutOfBoundsException("Invalid coordinate $index")
    }
}

fun main() {
    val p = MutablePoint(10, 20)
    p[1] = 30
    println(p[1])
    // 20
}

어떤 객체가 컬렉션에 들어있는지 검사 (in 관례)

  • in : 객체가 컬렉션에 들어있는지 검사
  • 대응하는 함수 : contain()
data class Point(val x: Int, val y: Int)

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

operator fun Rectangle.contains(p: Point): Boolean {
    return p.x in upperLeft.x ..< lowerRight.x && p.y in upperLeft.y ..< lowerRight.y
    // ..< : 열린 범위
}

fun main() {
    val rect = Rectangle(Point(10, 20), Point(50, 50))
    println(Point(20, 20) in rect)
    // true
    println(Point(5, 5) in rect)
    // false
}
  • in 오른쪽에 있는 객체 → contain 메서드의 수신 객체
  • in 오른쪽에 있는 객체 → contain 메서드에 인자로 전달

객체로부터 범위 만들기 (rangeTo, rangeUntil 관례)

rangeTo 연산자

  • 닫힌 범위를 만듦 (상계 값 포함 o)
  • .. 연산자 → rangeTo 함수 호출로 컴파일
start .. end -> start.rangeTo(end)
  • rangeTo 함수 → 범위를 반환함

  • 어떤 클래스가 Comparable 인터페이스를 구현한다면 rangeTo를 정의할 필요가 없음

  • 코틀린 표준 라이브러리 → 모든 Comparable 객체에 대해 적용 가능한 rangeTo 함수가 들어있음

operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>
import java.time.LocalDate

fun main() {
    val now = LocalDate.now()
    val vacation = now .. now.plusDays(10)
    println(now.plusDays(1) in vacation)
    // true
}
  • now .. now.plusDays(10) → now.rangeTo(now.plusDays(10)) 으로 변환
  • rangeTo 연산자는 다른 산술 연산자들 보다 우선순위가 낮음

rangeUntil 연산자

  • 열린 범위를 만듦 (상계 값 포함 x)
fun main() {
		(0 ..< 9).forEach{ println(it) }
}

자신의 타입에 대해 루프 수행 (iterator 관례)

  • 코틀린의 for 루프 → in 연산자를 사용
    • 하지만 contain 함수가 아닌 iterator 함수를 호출
    • iterator(를 호출해 이터레이터를 얻은 다음 그 이터레[이터에 대해 hashNext와 next 호출을 반복
import java.time.LocalDate

operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =
    object : Iterator<LocalDate> {
        var current = start
        override fun hasNext(): Boolean = current <= endInclusive
        override fun next(): LocalDate {
            val thisDate = current
            current = current.plusDays(1)
            return thisDate
        }
    }

fun main() {
    val newYear = LocalDate.ofYearDay(2042, 1)
    val daysOff = newYear.minusDays(1) .. newYear
    for(dayOff in daysOff) {println(dayOff)}
    // 2041-12-31
		// 2042-01-01
}

0개의 댓글