클래스 대리자를 통해 상속을 합성으로 대체할 수 있고, 속성 대리자를 통해 어떤 속성의 getter, setter를 다른 클래스에 있는 속성의 getter, setter를대체할 수 있다.
다른 클래스의 인스턴스가 포함된 클래스를 만들고, 그 클래스에 연산을 위임하고 싶은 경우 연산을 위임할 메서드가 포함된 인터페이스를 만들고, 클래스에서 해당 인터페이스를 구현한 다음 by 키워드를 사용해 바깥쪽 래퍼 클래스를 만든다.
interface Dialable {
fun dial(number: String): String
}
class Phone : Dialable {
override fun dial(number: String): String = "Dialing $number"
}
interface Snappable {
fun takePictrue(): String
}
class Camera : Snappable {
override fun takePictrue() = "Taking Picture"
}
class SmartPhone(
private val phone: Dialable = Phone(),
private val camera: Snappable = Camera()
) : Dialable by phone, Snappable by camera
생성자에서 Phone, Camera를 인스턴스화 하고 모든 public 함수를 Phone, Camera 인스턴스를 위임하도록 Dialable by phone, Snappeable by camera by
키워드를 사용했다
class SmartPhoneTest {
@Test
internal fun `dialing delegates to internal phone`() {
val smartPhone = SmartPhone()
val dial = smartPhone.dial("111")
println(dial) // Dialing 111
}
@Test
internal fun `Taking picture delegates to internal camera`() {
val smartPhone = SmartPhone()
val message = smartPhone.takePictrue()
println(message) // Taking Picture
}
}
by 키워드를 통해 위임 받은 함수를 호출할 수 있게 된다. 만약 by 키워드를 사용해 위임하지 않았다면 Dialable, Snappable를 구현하고 있는 구현 클래스 SmartPhone에서 각 상위 클래스의 세부 구현을 진행해야 한다. 이 구현을 by 키워드를 통해 위임했다.
어떤 속성이 필요할 때까지 해당 속성의 초기화를 지연시키고 싶은 경우 코틀린 표준 라이브러리의 lazy 대리자를 사용 가능하다.
여러 값이 들어 있는 맵을 제공해 객체를 초기화 하곳 싶은 경우 코틀린 맵에서 대리자가 되는 데 필요한 getValue, setValue 함수를 구현할 수 있다.
class Project(val map: MutableMap<String, Any>) {
val name: String by map
val priority: Int by map
val completed: Boolean by map
}
class ProjectTest {
@Test
internal fun `use map delegate for project`() {
val project = Project(
mutableMapOf(
"name" to "Lean Kotlin",
"priority" to 5,
"completed" to true
)
)
println(project)
// Project(map={name=Lean Kotlin, priority=5, completed=true})
}
}
Project 생성자는 MutableMap을 인자로 받아서 해당 맵의 키의 해당하는 값으로 Project 클래스의 모든 속성을 초기화한다. 이 코드가 동작하는 이유는 MutableMap에 ReadWriteProperty 대리자가 되는 데 필요한 올바른 시그니처의 setValue, getValue 확장 함수가 있기 때문이다.