by를 사용한 위임
Coddy Kotlin 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 46번째.
위임은 객체가 작업을 상속하는 대신 다른 객체에 넘기는 것을 의미합니다. Kotlin은 이를 직접 지원합니다: class Polite(private val inner: Greeter) : Greeter by inner는 모든 호출을 inner로 전달하여 Greeter를 구현하며, 클래스는 변경하는 부분만 재정의합니다:
interface Greeter {
fun hello(name: String): String
fun bye(name: String): String
}
class English : Greeter {
override fun hello(name: String) = "Hello, $name"
override fun bye(name: String) = "Bye, $name"
}
class Polite(private val inner: Greeter) : Greeter by inner {
override fun hello(name: String) = inner.hello(name) + ", pleased to meet you"
}main 내부에서:
val g = Polite(English())
println(g.hello("Ada"))
println(g.bye("Ada"))출력:
Hello, Ada, pleased to meet you
Bye, Ada위임은 상속의 대안입니다. 구현이 final인 인터페이스와 함께 작동하며, 위임 대상은 프로그램 실행 중에 선택되는 어떤 객체든 될 수 있습니다. 이 집합은 모든 add 호출을 세고 나머지는 일반 MutableSet으로 전달합니다:
class CountingSet<T>(private val inner: MutableSet<T> = mutableSetOf()) : MutableSet<T> by inner {
var attempts = 0
private set
override fun add(element: T): Boolean {
attempts++
return inner.add(element)
}
}main 내부:
val tags = CountingSet<String>()
tags.add("kotlin")
tags.add("jvm")
tags.add("kotlin")
println("${tags.size} tags after ${tags.attempts} adds, contains jvm: ${"jvm" in tags}")출력:
2 tags after 3 adds, contains jvm: true위임된 프로퍼티는 다시 by를 사용하여 프로퍼티의 getter와 setter를 다른 객체에 맡깁니다. 2장의 lazy가 이러한 위임자 중 하나이며, 맵도 또 다른 위임자가 될 수 있습니다. 각 프로퍼티는 자신의 이름으로 된 항목을 읽습니다:
class User(values: Map<String, Any?>) {
val name: String by values
val age: Int by values
}main 내부에서:
val u = User(mapOf("name" to "Ada", "age" to 36))
println("${u.name} is ${u.age}")출력:
Ada is 36직접 프로퍼티 위임자를 작성할 수 있습니다. operator fun getValue를 포함하는 클래스이며, var에는 setValue도 추가합니다. Kotlin은 프로퍼티를 읽고 쓸 때마다 이들을 호출합니다. 다음 위임자는 할당된 모든 텍스트에서 공백을 제거하고 변경 횟수를 셉니다:
import kotlin.reflect.KProperty
class Trimmed {
private var value = ""
var changes = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue.trim()
changes++
}
}
class Form {
val nameField = Trimmed()
var name: String by nameField
}main 내부에서:
val form = Form()
form.name = " Ada "
form.name = "Ada L. "
println("[${form.name}] after ${form.nameField.changes} changes")출력:
[Ada L.] after 2 changes챌린지
쉬움인터페이스 Storage(put, get 및 keys 포함)와 그 구현인 MapStorage가 제공됩니다. 클래스 위임을 사용하여 각각 put만 재정의하는 두 개의 데코레이터를 작성하세요. LoggingStorage(inner)는 읽기 전용 log 목록에 put a=1을 기록하고 호출을 전달하며, LimitedStorage(inner, max)는 max개의 키가 존재할 때 새 키를 추가하려는 요청을 거부합니다(false 반환). 기존 키의 업데이트는 계속 허용됩니다. 그 외의 모든 것은 by를 사용하여 전달합니다.
제공된 코드는 MapStorage를 LoggingStorage로 감싸며, 첫 번째 줄이 양수 제한일 경우 다시 LimitedStorage로 감쌉니다. put a 1, get a 및 keys 명령을 실행하여 ok, refused, 값 또는 missing, 그리고 키 목록을 출력하고, 마지막으로 log: put a=1, ...을 출력합니다.
코드는 LoggingStorage.kt, LimitedStorage.kt, Storage.kt 및 MapStorage.kt에 작성하세요. Main.kt에는 제공된 입출력 코드가 포함되어 있으며 수정할 수 없습니다.
직접 해보기
fun main() {
// 제공된 입력/출력 코드: 그대로 유지하세요
val input = generateSequence(::readLine).toList()
val base = MapStorage()
val logging = LoggingStorage(base)
val limit = input[0].toInt()
val storage: Storage = if (limit > 0) LimitedStorage(logging, limit) else logging
for (cmd in input.drop(1)) {
val p = cmd.split(" ")
when (p[0]) {
"put" -> println(if (storage.put(p[1], p[2])) "ok" else "refused")
"get" -> println(storage.get(p[1]) ?: "missing")
else -> println(storage.keys())
}
}
println("log: " + logging.log.joinToString(", ").ifEmpty { "empty" })
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: Kotlin 플레이그라운드