Chapter7. 연산자 오버로딩과 기타 관례 (2)

김신영·2022년 10월 22일
0

kotlin-in-action

목록 보기
9/11
post-thumbnail

구조 분해 선언과 component 함수

  • componentN 함수

  • val (a, b) = p --> val a = p.component1() , val b = p.component2()

  • data 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어 준다.

    	- 배열이나 컬렉션에는 Kotlin 표준 라이브러리에서 맨 앞의 다섯 원소에 대한 `componentN` 함수를 제공한다.
data class Point(val x: Int, val y: Int)  
  
fun main(args: Array<String>) {  
    val p = Point(10, 20)  
    val (x, y) = p  
    println(x)  // 10
    println(y)  // 20
}
data class NameComponents(  
        val name: String,  
        val extension: String)  
  
fun splitFilename(fullName: String): NameComponents {  
    val (name, extension) = fullName.split('.', limit = 2)  
    return NameComponents(name, extension)  
}  
  
fun main(args: Array<String>) {  
    val (name, ext) = splitFilename("example.kt")  
    println(name)  // example
    println(ext)   // kt
}

구조 분해 선언(Destructing declaration) 과 루프

  • Kotlin 라이브러리는 Map.Entry 에 대한 확장 함수로 component1, component2 를 제공한다.
fun printEntries(map: Map<String, String>) {  
    for ((key, value) in map) {  
        println("$key -> $value")  
    }  
}  
  
fun main(args: Array<String>) {  
    val map = mapOf("Oracle" to "Java", "JetBrains" to "Kotlin")  
    printEntries(map)  
}

// Oracle -> Java
// JetBrains -> Kotlin

Delegated Property (위임 프로퍼티)

  • delegation 패턴
class Foo {
  var p: Type by Delegate()
}
class Delegate {
    operator fun getValue(object: Foo, property: KProperty<*>): Type {
	    // ...
    }

    operator fun setValue(object: Foo, property: KProperty<*>, newValue: Type) {
	    // ...
	}
}

위임 프로퍼티 컴파일 규칙

  • val x = c.prop --> val x = delegateObj.getValue(c, kPropertyObj)
  • c.prop = x --> delegateObj.setValue(c, kPropertyObj, x)

ReadOnlyProperty<R, T> , ReadWriteProperty<R, T>

public interface ReadOnlyProperty<in R, out T> {  
     public operator fun getValue(thisRef: R, property: KProperty<*>): T  
}  
  
public interface ReadWriteProperty<in R, T> {  
     public operator fun getValue(thisRef: R, property: KProperty<*>): T  
  
     public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)  
}

Lazy Initialization (lazy 함수)

class Email { /*...*/ }  
fun loadEmails(person: Person): List<Email> {  
    println("Load emails for ${person.name}")  
    return listOf(/*...*/)  
}  
  
class Person(val name: String) {  
    val emails by lazy { loadEmails(this) }  
}  
  
fun main(args: Array<String>) {  
    val p = Person("Alice")  
    println(p.emails)  
    println(p.emails)  
}

// Load emails for Alice
// []
// []

lazy 함수

public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

public interface Lazy<out T> {  
    public val value: T  
    public fun isInitialized(): Boolean  
}

프로퍼티 값을 맵에 저장

  • Expando Object (확장 가능한 객체)
    - 자신의 프로퍼티를 동적으로 정의할 수 있는 객체
class Person {  
    private val _attributes = hashMapOf<String, String>()  
  
    fun setAttribute(attrName: String, value: String) {  
        _attributes[attrName] = value  
    }  
  
    val name: String by _attributes  
}  
  
fun main(args: Array<String>) {  
    val p = Person()  
    val data = mapOf("name" to "Dmitry", "company" to "JetBrains")  
    for ((attrName, value) in data)  
       p.setAttribute(attrName, value)  
    println(p.name)  
}

위임 프로퍼티

  • 프로퍼티 변경될 때 마다, 리스너에게 변경 통지를 구현해보자.

backing property를 통한 구현

open class PropertyChangeAware {  
    protected val changeSupport = PropertyChangeSupport(this)  
  
    fun addPropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.addPropertyChangeListener(listener)  
    }  
  
    fun removePropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.removePropertyChangeListener(listener)  
    }  
}  
  
class Person(  
        val name: String, age: Int, salary: Int  
) : PropertyChangeAware() {  
    var age: Int = age  
        set(newValue) {  
            val oldValue = field  
            field = newValue  
            changeSupport.firePropertyChange(  
                    "age", oldValue, newValue)  
        }  
  
    var salary: Int = salary  
        set(newValue) {  
            val oldValue = field  
            field = newValue  
            changeSupport.firePropertyChange(  
                    "salary", oldValue, newValue)  
        }  
}  
  
fun main(args: Array<String>) {  
    val p = Person("Dmitry", 34, 2000)  
    p.addPropertyChangeListener(  
        PropertyChangeListener { event ->  
            println("Property ${event.propertyName} changed " +  
                    "from ${event.oldValue} to ${event.newValue}")  
        }  
    )  
    p.age = 35  
    p.salary = 2100  
}

위임 프로퍼티를 통해 구현

open class PropertyChangeAware {  
    protected val changeSupport = PropertyChangeSupport(this)  
  
    fun addPropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.addPropertyChangeListener(listener)  
    }  
  
    fun removePropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.removePropertyChangeListener(listener)  
    }  
}  
  
class ObservableProperty(  
    var propValue: Int, val changeSupport: PropertyChangeSupport  
) {  
    operator fun getValue(p: Person, prop: KProperty<*>): Int = propValue  
  
    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {  
        val oldValue = propValue  
        propValue = newValue  
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)  
    }  
}  
class Person(  
    val name: String, age: Int, salary: Int  
) : PropertyChangeAware() {  
  
    var age: Int by ObservableProperty(age, changeSupport)  
    var salary: Int by ObservableProperty(salary, changeSupport)  
}  
  
fun main(args: Array<String>) {  
    val p = Person("Dmitry", 34, 2000)  
    p.addPropertyChangeListener(  
        PropertyChangeListener { event ->  
            println("Property ${event.propertyName} changed " +  
                    "from ${event.oldValue} to ${event.newValue}")  
        }  
    )  
    p.age = 35  
    p.salary = 2100  
}

Delegates.observable을 통해 구현 (코틀린 표준 라이브러리)

open class PropertyChangeAware {  
    protected val changeSupport = PropertyChangeSupport(this)  
  
    fun addPropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.addPropertyChangeListener(listener)  
    }  
  
    fun removePropertyChangeListener(listener: PropertyChangeListener) {  
        changeSupport.removePropertyChangeListener(listener)  
    }  
}  
  
class Person(  
    val name: String, age: Int, salary: Int  
) : PropertyChangeAware() {  
  
    private val observer = {  
        prop: KProperty<*>, oldValue: Int, newValue: Int ->  
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)  
    }  
  
    var age: Int by Delegates.observable(age, observer)  
    var salary: Int by Delegates.observable(salary, observer)  
}  
  
fun main(args: Array<String>) {  
    val p = Person("Dmitry", 34, 2000)  
    p.addPropertyChangeListener(  
        PropertyChangeListener { event ->  
            println("Property ${event.propertyName} changed " +  
                    "from ${event.oldValue} to ${event.newValue}")  
        }  
    )  
    p.age = 35  
    p.salary = 2100  
}

요약

  • equals, compareTo
  • get, set, contains
  • rangeTo, iterator
  • componentN
  • delegate property by
  • lazy
  • Delegates.obeservable
  • expando object (delegate property by map)
profile
Hello velog!

0개의 댓글