ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin] by 키워드
    Kotlin/늦은 초기화 2021. 9. 3. 16:23

    1. by를 통한 클래스의 인터페이스 구현을 다른 객체에게 위임

    interface A {...}
    class B : A {...}
    class C(b : B) : A by b {...}

     

    클래스 C는 인터페이스 A를 구현한다.

    클래스 C의 인터페이스 A 구현은 b객체(B클래스)가 A를 구현한 방식으로 구현된다.

    클래스 C의 인터페이스 A 구현을 b객체에게 위임한다.

     

    인터페이스의 메서드의 경우

     

    interface Soundable {
        fun sound()
    }
    
    class Dog : Soundable {
        override fun sound() {
            println("멍멍")
        }
    }
    
    class MyDog(dog: Dog) : Soundable by dog {
    }
    
    fun main() {
        val dog = Dog()
        val myDog = MyDog(dog)
        myDog.sound() //멍멍
    }

    MyDog클래스의 Soundable 구현은 dog객체(Dog클래스)가 Soundable을 구현한 것처럼 구현된다.

    바이트 코드를 디컴파일 해보면

     

    내부적으로 MyDog 클래스에 Dog 멤버변수가 선언되어 있고 생성자에서 넘어간 dog인스턴스가 대입된다.

    또한 MyDog의 Soundable에 해당하는 sound 메서드구현은 내부적으로 멤버변수 dog의 sound 메서드를 호출한다.

     

    상속이 아닌 위임을 사용할 때 인터페이스의 추상메서드가 많아지면 해당되는 메서드 모두 내부적으로 dog의 메서드를 호출하는 코드를 손수 작성해야할 것이다.

     

     

    by 를 사용하면 Soundable 인터페이스에 해당하는 추상 메서드만 구현된다.

    아래의 Dog에서 독자적으로 선언한 fun method()는 by를 통해 인터페이스 구현을 위임하였을 때 MyDog클래스에는 포함되지 않는다.

     

    위임을 통해 구현된 메서드를 사용하고 싶지 않은경우에는 

    해당하는 추상메서드를 클래스 내부에서 오버라이딩 하면 된다.

    디컴파일 된 코드

     

     

    by 객체 -> 객체를 인식할 수만 있으면 다음과 같이 변경할 수 있다.


     

    인터페이스의  프로퍼티는 어떻게 구현될까?

     

    코틀린에서 프로퍼티를 어떻게 정의하느냐에 따라 자바 필드(backing-field)유무가 달라진다.

    인터페이스의 프로퍼티는 자바 인터페이스의 getter,setter로 처리되므로

    위임을 통해 인터페이스 구현시 getter,setter메서드도 같이 구현된다.

     

    interface Soundable {
        var count: Int //짖은 횟수
        fun sound()
    }
    
    class Dog : Soundable {
    
        override var count: Int = 0
    
        override fun sound() {
            println("멍멍")
            count++
        }
    }
    
    class MyDog(dog: Dog) : Soundable by dog {
    }
    
    fun main() {
    
        val dog = Dog()
        val myDog = MyDog(dog)
        myDog.sound() //멍멍
        myDog.count //1
    }

     

    Soundable의 count 프로퍼티는 인터페이스 내부에 getter,setter 메서드로 처리되는 것을 확인할 수 있다.

     

    Soundable을 구현한 Dog클래스를 보면 프로퍼티 정의문법에 따라 backing-field가 존재하고

    Soundable의 getter,setter를 오버라이딩 한 것으로 처리된다.

     

    인터페이스의 프로퍼티도 메서드로 처리되기 때문에 dog객체(Dog클래스)를 통해 Soundable구현을 위임시키면 

    getter, setter또한 인터페이스의 추상 메서드로 간주되므로, Dog클래스의 getter,setter로 자동으로 구현시켜준다.


    2. by를 통한 프로퍼티의 Accesor(getter,setter)구현을 다른 객체에게 위임

    The syntax

    val/var <property name>: <Type> by <expression>

    expression에 해당하는 것은 delegate이다. delegate는 사용자 정의 클래스가 될 수 있다.

    프로퍼티의 getter는 delegate클래스의 getValue()메서드에게 위임한다.

    프로퍼티의 setter는 delegate클래스의 setValue()메서드에게 위임한다.

    즉. delegate(대리자)에 정의된 getValue, setValue가 property의 get() (getter), set() (setter)로 동작하게 된다.

     

    var프로퍼티 by 위임클래스 -> 위임 클래스에 getValue(), setValue() 메서드를 선언해야 한다.

    val프로퍼티 by 위임클래스 -> 위임 클래스에 getValue() 메서드를 선언해야 한다.

    또한 operator가 붙어야한다.

     

    위임 클래스의 getValue,setValue로부터 타입 추론이 가능하여 Type을 생략가능하다.

    val str by 위임클래스

     

    참고: 연산자 오버로딩 부분의 프로퍼티 위임에 해당하는 메서드

    https://kotlinlang.org/docs/operator-overloading.html#property-delegation-operators

    연산자 오버로딩에서 property 위임에 해당하는 메서드 형식

    operator fun getValue()

    operator fun setValue(...) 이다. 

     

    Operator overloading | Kotlin

     

    kotlinlang.org

     

    프로퍼티의 getter,setter로직이 복잡하거나, 재사용가능성이 많은 프로퍼티라면 클래스로 만들어 쓸 수 있는 것이다.


     

    아래의 코드는 기본 구현 getter,setter와 똑같이 동작하도록 해놓은 코드이다.

    아래의 코드처럼 사용하면 by를 사용할 필요가 없지만 쉬운 이해를 위해 써놨다.

    getValue와 setValue는 프로퍼티 getter, setter문법과 다르므로

    기본 구현 getter, setter처럼 사용하려면 상태를 저장할 임시 변수를 DelegateHelp클래스 내부에 선언해야 한다.

    val temp:String을 선언하여 setValue호출시 temp에 저장하고 getValue시 temp의 값이 사용된다.

     

    import kotlin.reflect.KProperty
    
    class Example {
    
        var str: String by DelegateHelper()
        //DelegateHelp클래스에게 getter,setter 처리 위임
    }
    
    class DelegateHelper() {
    
        var temp:String="초기값"
    
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            println("getValue 호출")
            return temp
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("setValue 호출")
            println("인자 value: $value")
            temp=value
        }
    
    }
    
    fun main() {
        val example = Example()
        println(example.str) 
        example.str="값 대입" 
        println(example.str) 
    
    }

    --getValue, setValue의 파라메터

    thisRef -프로퍼티 str을 포함하는 val example=Example()의 example 인스턴스에 해당

    property - str프로퍼티의 정보(값이 아닌 프로퍼티의 정보이다)

     

    --setValue의 파라메터

    value - 코드로 보자

    var str:String by HelperClass()
    str="값 대입"

    str="값 대입"으로부터 setValue의 value인자로 "값 대입"이 넘어온다.

     

    인자 테스트

    package com.test.kotlinpractice.delegation
    
    import kotlin.reflect.KProperty
    
    class Example {
    
        var str: String by DelegateHelper()
        //DelegateHelp클래스에게 getter,setter 처리 위임
    }
    
    class DelegateHelper() {
    
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            println("getValue 인자")
            println("thisRef.toString(): ${thisRef.toString()}")
            println("property.name: ${property.name}")
            return "테스트"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("setValue 인자")
            println("두개는 getValue 인자와 같음")
            println("value: $value")
        }
    
    }
    
    fun main() {
        val example = Example()
        println(example.str)
        example.str="값 대입"
    
    }

    Kotlin standard library provides factory methods for several useful kinds of delegates.

    https://kotlinlang.org/docs/delegated-properties.html#standard-delegates

     

    Delegated properties | Kotlin

     

    kotlinlang.org

     

     

     

     

     

     

     

     

    'Kotlin > 늦은 초기화' 카테고리의 다른 글

    [Kotlin] by lazy  (0) 2021.12.12

    댓글

Designed by Tistory.