Menu
Coddy logo textTech

컴패니언 객체

Coddy Kotlin 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 13번째.

일부 멤버는 하나의 인스턴스가 아니라 클래스 전체에 속합니다. Kotlin에서는 클래스 내부의 companion object에 이러한 멤버를 넣으며, 클래스 이름을 통해 호출합니다:

class Temperature(val celsius: Double) {
    companion object {
        fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
    }
}

main 내부에서:

val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)

출력:

100.0

fromFahrenheit는 팩토리 함수입니다. 즉, 인스턴스를 생성하는 함수입니다. 생성자와 달리 설명적인 이름을 가지며, 객체 대신 null을 반환할 수 있습니다. private constructor를 사용하면 팩토리만 접근할 수 있으므로, 먼저 입력을 확인하고 정리할 수 있습니다:

class Username private constructor(val text: String) {
    companion object {
        fun of(raw: String): Username? {
            val clean = raw.trim().lowercase()
            return if (clean.length in 3..12) Username(clean) else null
        }
    }
}

main 내부:

println(Username.of("  Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x")   // 오류: 생성자가 private입니다

출력:

ada_l
null

동반 객체는 클래스에 속하므로 private 생성자를 호출할 수 있으며, 클래스 자체의 코드는 접두사 없이 동반 객체의 멤버를 사용합니다. 여기서는 모든 티켓이 하나의 카운터를 공유하고, 각 티켓이 그 카운터에서 다음 번호를 가져옵니다:

class Ticket(val holder: String) {
    val number = nextNumber++

    companion object {
        private var nextNumber = 1
        val issued: Int
            get() = nextNumber - 1
    }
}

main 내부:

val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")

출력:

Ada #1, Bo #2
issued: 2

Kotlin에는 static 키워드가 없습니다. 컴패니언 객체의 멤버가 그 역할을 합니다. 즉, 클래스 전체에 하나의 복사본이 존재하며 ClassName.member로 접근합니다. 다른 객체와 마찬가지로 컴패니언 객체는 클래스가 처음 사용될 때 한 번 생성됩니다. 팩토리와 공유 카운터처럼 하나의 인스턴스에 의존하지 않는 것을 그 안에 넣고, 인스턴스의 데이터를 읽는 것은 모두 일반 멤버로 둡니다.

class User private constructor(val name: String) {
    fun greet() = "hi $name"             // 인스턴스가 필요함: user.greet()

    companion object {
        fun guest() = User("guest")      // 클래스에 속함: User.guest()
    }
}
challenge icon

챌린지

쉬움

Coupon 클래스를 완성하세요. 생성자는 private이므로 쿠폰은 companion object의 팩토리 Coupon.create(code, percent)를 통해 생성됩니다. code가 대문자와 숫자로 이루어진 4~10자의 문자열이 아니거나 percent가 1에서 90 사이가 아니면 null을 반환합니다. companion object는 생성한 쿠폰의 개수도 created에 저장하며, 외부 코드에서는 이 값을 읽을 수 있지만 변경할 수는 없습니다. apply(price)는 할인된 가격을 내림하여 반환합니다(price * (100 - percent) / 100).

제공된 코드는 SAVE10 10 200(code, percent, price)과 같은 줄을 읽습니다. SAVE10: 180 또는 invalid coupon SAVE10을 출력하고, 마지막으로 개수와 함께 created: 를 출력합니다.

코드는 Coupon.kt에 작성하세요. Main.kt에는 제공된 입력/출력 코드가 있으며 수정할 수 없습니다.

직접 해보기

fun main() {
    // 제공된 입력/출력 코드: 그대로 유지하세요
    val input = generateSequence(::readLine).toList()
    for (line in input) {
        val (code, percent, price) = line.split(" ")
        val coupon = Coupon.create(code, percent.toInt())
        if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
    }
    println("created: ${Coupon.created}")
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: Kotlin 플레이그라운드