[기초편] Kotlin 제6강 - 의뢰류와 속성

12872 단어

위탁류


하나의 인터페이스를 실현하고by 키워드를 사용하여 인터페이스를 다른 대상에게 의뢰할 수 있습니다.
interface OnClickListener{
    fun onClick()

    fun onLongClick()
}

class ViewClickDelegate : OnClickListener{

    override fun onClick(){
        println("ViewClickDelegate onClick")
    }

    override fun onLongClick() {
        println("ViewClickDelegate onLongClick")
    }
}

class View(val name: String, onClickListener: OnClickListener) : OnClickListener by onClickListener{

    override fun onLongClick() {
        println("$name onLongClick")
    }
}

클래스 의뢰 후에도 우리는 다시 쓰는 방식으로 의뢰 클래스의 실현을 덮어쓸 수 있습니다. 여기에서View는onLongClick 방법을 실현하고 ViewClickDelegate 클래스의onLongClick 방법을 덮어씁니다.
클래스 의뢰의 본질은 추상적인 방법의 실현을by후의 의뢰 대상에게 맡긴다는 것이다

초기화 및 위임 지연 속성


초기화 지연 속성


대상이 만들어질 때 초기화되지 않고 처음 사용할 때 초기화됩니다.완성 후 일반 속성처럼 사용
open class Food(val name: String) {

    override fun toString(): String {
        return "[$name]"
    }
}

class Container(val name: String) {

    lateinit var foodList: List

}

불활성 초기화 속성


이 속성을 처음 사용할 때 초기화되고 한 번만 초기화됩니다.초기화 여부를 기호로 표시하면 기호는 여러 가지 선택과 실현 방식이 있다.
코드 정의에서 초기화를 실행하면 코드 유지보수에 도움이 됩니다.
지령식 언어에 대해 이 모델은 위험, 특히 공유 상태를 사용하는 프로그램 습관을 잠재하고 있을 수 있다.

일반적 실현

class Container2(val name: String) {
    private var _foodList: List? = null
    val foodList: List
        get() {
            if (_foodList == null) {
                _foodList = arrayListOf(Food(" "))
            }
            return _foodList!!
        }
}

by lazy () {} 불활성 초기화

class Container4(val name: String) {
    val food: Food by lazy{
        Food(" ")
    }
}

// 
class Container5(val name: String) {
    val food: Food by lazy(Container5::class){
        Food(" ")
    }
}

//    SYNCHRONIZED
//PUBLICATION, , 
class Container6(val name: String) {
    val food: Food by lazy(LazyThreadSafetyMode.SYNCHRONIZED){
        Food(" ")
    }
}

자바빈의 디자인에서 속성에 따라 네 가지 유형으로 나뉜다. 그것이 바로 단값 속성, 인덱스 속성이다.연관 속성, 제한 속성.Kotlin이 연관 및 제한 속성을 구현하는 방법

연관 속성(관찰 가능 속성)


PropertyChangeSupport 코드를 통한 속성 스니핑

class Shelf(val name: String, _book: Book) {

    private val propertyChange: PropertyChangeSupport = PropertyChangeSupport(this)
    var book: Book = _book
        set(value) {
            val oldBook = field
            field = value
            propertyChange.firePropertyChange("book", oldBook, value)
        }


    fun addBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        propertyChange.addPropertyChangeListener("book", propertyChangeListener)
    }

    fun removeBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        propertyChange.removePropertyChangeListener("book", propertyChangeListener)
    }
}

논리를 봉하여 기류를 추출하다
open class BasePropertyChange {

    val propertyChange = PropertyChangeSupport(this)

    protected fun addChangeListener(key: String, propertyChangeListener: PropertyChangeListener) {
        propertyChange.addPropertyChangeListener(key, propertyChangeListener)
    }

    protected fun removeChangeListener(key: String, propertyChangeListener: PropertyChangeListener) {
        propertyChange.removePropertyChangeListener(key, propertyChangeListener)
    }
}

class Shelf_2(val name: String, _book: Book) : BasePropertyChange() {

    var book: Book = _book
        set(value) {
            val oldBook = field
            field = value
            propertyChange.firePropertyChange("book", oldBook, value)
        }

    fun addBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        addChangeListener("book", propertyChangeListener)
    }

    fun removeBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        removeChangeListener("book", propertyChangeListener)
    }
}

책의 set 액세서리의 논리를 클래스로 봉합하기
class BookDelegate(_book: Book, val propertyChange: PropertyChangeSupport) {

    var field: Book = _book

    fun getValue(): Book = field

    fun setValue(value: Book) {
        val oldBook = field
        field = value
        propertyChange.firePropertyChange("book", oldBook, value)
    }
}

class Shelf2(val name: String, _book: Book) : BasePropertyChange() {

    val _bookDelegate: BookDelegate = BookDelegate(_book, propertyChange)
    var book: Book
        set(value) {
            _bookDelegate.setValue(value)
        }
        get() = _bookDelegate.getValue()

    fun addBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        addChangeListener("book", propertyChangeListener)
    }

    fun removeBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        removeChangeListener("book", propertyChangeListener)
    }
}


이로써 우리는 Kotlin으로 수동으로 속성 변화를 관찰할 수 있는 기능을 실현하여 테스트하였다.
fun testObserverField() {
    val shelf = Shelf2(" ", Book("Think in java"))
    shelf.addBookChangeListener(object : PropertyChangeListener {
        override fun propertyChange(evt: PropertyChangeEvent?) {
            val oldBook = evt?.oldValue as Book
            val newBook = evt.newValue as Book

            println("old book = $oldBook , new book = $newBook")
        }
    })

    shelf.book = Book("Kotlin in action")
}

위 코드 실행 결과는 다음과 같습니다.
old book = Book(name=Think in java) , new book = Book(name=Kotlin in action)

Kotlin 위임으로 구현


Kotlin의 의뢰 속성은 언어 차원에서 속성의 읽기 전용기에서 의뢰 클래스에서operator가 수식하는 두 가지 인자 getValue 방법을 호출하고 속성 쓰기 전용기에서operator가 setValue를 수식하는 세 가지 인자 방법을 호출합니다
class BookDelegate2(_book: Book, val propertyChange: PropertyChangeSupport) {

    var field: Book = _book

    operator fun getValue(shelf3: Shelf3, prop: KProperty): Book = field

    operator fun setValue(shelf3: Shelf3, prop: KProperty, newValue: Book) {
        val oldBook = field
        field = newValue
        propertyChange.firePropertyChange("book", oldBook, newValue)
    }
}

class Shelf3(val name: String, _book: Book) : BasePropertyChange() {

    var book: Book by BookDelegate2(_book, propertyChange)

    fun addBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        addChangeListener("book", propertyChangeListener)
    }

    fun removeBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        removeChangeListener("book", propertyChangeListener)
    }
}

일반적으로 ReadWriteProperty 인터페이스를 통해 의뢰를 쉽게 수행할 수 있습니다
class BookDelegate3(var field: Book, val propertyChange: PropertyChangeSupport) : ReadWriteProperty {

    override fun getValue(thisRef: Shelf3_1, property: KProperty): Book {
        return field
    }

    override fun setValue(thisRef: Shelf3_1, property: KProperty, value: Book) {
        val oldBook = field
        field = value
        propertyChange.firePropertyChange("book", oldBook, value)
    }
}

class Shelf3_1(val name: String, _book: Book) : BasePropertyChange() {

    var book: Book by BookDelegate3(_book, propertyChange)

    fun addBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        addChangeListener("book", propertyChangeListener)
    }

    fun removeBookChangeListener(propertyChangeListener: PropertyChangeListener) {
        removeChangeListener("book", propertyChangeListener)
    }
}

위 코드를 테스트합니다.
fun testDelegateFieldForKotlin() {
    val shelf = Shelf3_1(" ", Book("Think in java"))

    shelf.addBookChangeListener(object : PropertyChangeListener {
        override fun propertyChange(evt: PropertyChangeEvent?) {
            val oldBook = evt?.oldValue as Book
            val newBook = evt?.newValue as Book

            println("Kotlin  old book is $oldBook, and new book is $newBook")
        }
    })

    shelf.book = Book("Kotlin in action!")
}

실행 결과는 다음과 같습니다.
Kotlin은 old book is Book(name=Think in java), and new book is Book(name=Kotlin in action!) 의뢰
위탁 속성의 본질: 속성 액세서리의 실현을 by 이후의 위탁 대상에게 건네주었다

Kotlin의 자체 구현을 사용하여 관찰 가능한 속성


사실,Delegate.observable () 클래스는 위에서 언급한 모든 논리를 실현했습니다.
델게이트 한번 봅시다.observable 방법의 원본 코드
public inline fun  observable(initialValue: T, crossinline onChange: (property: KProperty, oldValue: T, newValue: T) -> Unit):
        ReadWriteProperty = object : ObservableProperty(initialValue) {
            override fun afterChange(property: KProperty, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }

이 방법은 Observable Property 대상을 되돌려줍니다. Observable Property 대상의 원본 코드를 보십시오
public abstract class ObservableProperty(initialValue: T) : ReadWriteProperty {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty, oldValue: T, newValue: T): Boolean = true

    protected open fun afterChange (property: KProperty, oldValue: T, newValue: T): Unit {}

    public override fun getValue(thisRef: Any?, property: KProperty): T {
        return value
    }

    public override fun setValue(thisRef: Any?, property: KProperty, value: T) {
        val oldValue = this.value
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

이 대상은 getValue와 setValue 방법이 있습니다. 이것은 우리가 실현한 BookDelegate 3류의 getValue와 setValue 방법의 논리와 거의 같습니다.다른 점은 정부측에서 beforeChange () 제어와 애프터 Change () 를 사용하여 클래스 덮어쓰기를 할 수 있다는 것이다.
Kotlin 표준 라이브러리는 속성을 관찰할 수 있는 속성 의뢰를 제공합니다
class Shelf4(val name: String, _book: Book) {

    var book: Book by Delegates.observable(_book, {property, oldValue, newValue ->
        println("The old book's name is \"${oldValue.name}\", and the new book's name is \"${newValue.name}\"")
    })
}

상술한 코드를 테스트해 보다
fun testObserverFieldForKotlin(){
    val shelf = Shelf4(" ", Book("think in java"))
    shelf.book = Book("Kotlin in action")
}

실행 결과는 다음과 같습니다.
The old book's name is "think in java", and the new book's name is "Kotlin in action"

속성 제한


Kotlin 도 기존 의뢰 클래스를 제공하여 속성을 제한합니다
class Shelf5(val name: String, val book: Book ,_year: Int) {
    var year: Int by Delegates.vetoable(_year, {property, oldValue, newValue ->
        newValue <= 99
    })
}

위 코드 테스트
fun testVetoableFieldForKotlin(){
    val shelf = Shelf5(" ", Book("think in java"), 0)
    shelf.year = 200
    println("current book is ${shelf.year}")

    shelf.year = 20
    println("current book is ${shelf.year}")
}

실행 결과는 다음과 같습니다.
current book is 0 current book is 20
주의: 상기에서 사용한 것은 구성원 함수이며, 사실상 확장 함수도 위탁 속성을 실현할 수 있다

Map을 사용하여 위임 속성 구현


MapAccessors.kt 파일에는 다음과 같은 확장 함수 원본이 있습니다
@kotlin.jvm.JvmName("getVarContravariant")
@kotlin.internal.LowPriorityInOverloadResolution
@kotlin.internal.InlineOnly
public inline fun  MutableMap.getValue(thisRef: Any?, property: KProperty): V
        = @Suppress("UNCHECKED_CAST") (getOrImplicitDefault(property.name) as V)


@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.setValue(thisRef: Any?, property: KProperty, value: V) {
    this.put(property.name, value)
}

이를 통해 알 수 있듯이 MutableMap에는 getValue 방법과 setValue 방법이 존재하는데 위탁에 사용할 수 있다. 사실상 그렇다.
예를 들면 다음과 같습니다.
class Fruit(name: String) : Food(name){

    private val attributeMap = HashMap()

    val color: String by attributeMap

    val size: String by attributeMap

    fun setAttributeMap(name: String, value: String){
        attributeMap.put(name, value)
    }
}

fun testDelegateMap(){
    val fruit = Fruit(" ")

    fruit.setAttributeMap("color", " ")
    fruit.setAttributeMap("size", "2kg")

    println("color = ${fruit.color}, size = ${fruit.size}")
}

실행 결과
color = 그린, 크기 = 2kg

소결


클래스 의뢰의 본질은 추상적인 방법의 실현을by후의 의뢰 대상에게 맡긴다는 것이다
속성 의뢰의 본질은: 속성 액세서리의 실현을 by 이후의 의뢰 대상에게 맡긴다는 것이다.
확장 함수도 속성 의뢰 실현

참고 자료


위키백과: 불활성 초기화 모드
위키백과: 불활성 값 구하기

좋은 웹페이지 즐겨찾기