[번역] 코틀린에서의 위임

이현우·2021년 6월 21일
1

Deep Dive: Android

목록 보기
6/10
post-thumbnail

Kotlin's Delegation

다음 글은 A Java geek - A look at Kotlin's Delegation을 번역한 것입니다.

코틀린은 흥미로운 기능들을 많이 제공합니다. 대부분의 개발자들은 Null Safety를 다루는 부분에서 흥미를 가지는데, 저는 확장함수와 위임을 흥미롭게 보고 있습니다.

위임 패턴(The Delegation Pattern)

GOF 디자인 패턴에서 Delegation Pattern은 아래와 같이 정의하고 있습니다.

Delegation is a way to make composition as powerful for reuse as inheritance [Lie86, JZ91]. In delegation, two objects are involved in handling a request: a receiving object delegates operations to its delegate.

This is analogous to subclasses deferring requests to parent classes. But with inheritance, an inherited operation can always refer to the receiving object through the this member variable in C++ and self in Smalltalk.

To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver.

위임은 상속과 더불어 Composition(조립)을 만들기 위한 강력한 수단 중 하나이다. 위임에서는 두 객체가 한 요청을 처리한다.

이는 자식 클래스가 부모 클래스에게 자신의 요청을 전달하는 것과 유사한 방식입니다.

상속에서는 상속받은 동작들은 언제나 this를 통해 수신된 객체를 참조하게 됩니다.(C++에서는 this, Smalltalk에서는 self)

위임 패턴에서 이와 같은 동작을 수행하려면 동작을 대신하는 객체(이를 대리자라고 하자)에게 자신을 매개변수로 전달하고 연산을 이 매개변수로 전달한 객체에게 수행하라고 하면 된다.

위임은 Composition을 구성할 때 굉장히 중요합니다.

Java에서의 위임 vs Kotlin에서의 위임(Manual and native delegation)

Java에서 위임을 구현하려면 개발자 스스로 이를 구현해야 합니다.

interface A {
    void foo();
}

class B implements A {
    @Override
    public void foo() {
    }
}

class Composition implements A {
    private final B b;

    Composition(B b) {
        this.b = b;
    }

    @Override
    public void foo() {
        b.foo();
    }
}

하지만 Kotlin에서는 by라는 키워드로 위임을 언어 차원에서 구현할 수 있습니다. 위의 코드는 아래 코드와 동일합니다.

interface A {
    fun foo()
}

class B : A {
    override fun foo() {}
}

class Delegate(b: B) : A by b 

이 경우에는 foo() 함수는 B 클래스의 위임 객체에서만 호출될 수 있습니다.

Kotlin 공식 문서에서는 다음과 같이 설명하고 있습니다.

by clause는 b 객체가 위임 객체 내부에 저장될 것임을 알려주며 컴파일러는 B의 모든 메서드를 b의 것을 활용하여 생성한다.

위임 프로퍼티(Delegated Properties)

Kotlin은 위임 프로퍼티라는 것을 제공합니다. 위임 프로퍼티는 객체의 Getter Setter(var인 경우)를 다른 객체에게 위임하는 프로퍼티입니다. 이 경우에도 by 키워드를 사용합니다.

이런 기능들은 kotlin-stdlib를 통해 바로 사용할 수 있습니다.

커스텀 위임 프로퍼티(Your own delegated property)

만약 개발자가 직접 위임 프로퍼티를 만들고 싶다면 클래스에 다음과 같은 함수를 포함하면 됩니다.

  • operator fun <T, U> getValue(thisRef: T, prop: KProperty<*>): U
    • thisRef: 이 프로퍼티를 사용하는 클래스
    • prop: 객체의 reflection
    • U: 위임 프로퍼티의 방출형
  • operator fun <T, U> getValue(thisRef: T, prop: KProperty<*>, value: T)
    • setValue는 var 프로퍼티를 위임할 때 사용하는 함수이다
    • value 매개변수를 통해 들어온 값을 활용하여 property의 값을 set함
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}

위의 예시는 Delegates.NotNull()로 Delegate하는 위임 함수를 꺼내온 것입니다.

getValue, setValue를 통해 변수의 값을 설정하고 가져오는 것을 알 수 있습니다.

profile
이현우의 개발 브이로그

0개의 댓글