개발일지

Kotlin in A..Z (17) - 지연 초기화와 by 위임 본문

Kotlin (코틀린)

Kotlin in A..Z (17) - 지연 초기화와 by 위임

강태종 2020. 7. 15. 23:58

지연 초기화

인스턴스를 생성할 때 초기화를 할 필요가 없는 프로퍼티들이 존재한다. 그리고 인스턴스를 생성할 때 모든 프로퍼티를 초기화를 하는 것은 리소스에 부담을 줄 수 있다. 지연 초기화를 사용하여 프로퍼티를 사용하려 할 때 초기화를 할 수 있다.


- lateinit

  • primitive type에는 할 수 없다. (Int, Long, Double...)
  • var로 선언된 프로퍼티에만 가능하다
  • 프로퍼티에 대한 getter와 setter를 사용할 수 없다.
  • 초기화를 하지 않은 상태에서 접근하면 UninitializedpropertyAccessException을 발생한다.

 

코드

class Base {
    lateinit var hi: String

    fun action() {
        // 초기화가 됐는지 확인하는 방법
        if(::hi.isInitialized) {
            println(hi)
        }
        else {
            println("hi Is Not Initialized")
        }
    }

}
fun main() {
    val base = Base()

    // 아직 초기화가 되지 않은 상태
    base.action()

    // 초기화 진행
    base.hi = "개발일지"
    base.action()
}

 

결과

hi Is Not Initialized
개발일지

- lazy

  • 람다를 전달받아 Lazy<T> 형식의 객체를 반환하는 것이다.
  • 최초 getter에 lazy()에 전달받은 람다를 실행하여 저장하고 이후에 저장된 값을 반환한다.
  • value로 값을 접근할 수 있다.

코드

val lazySTR = lazy { "Hello" }
println(lazySTR.javaClass)
println(lazySTR.value)

 

결과

class kotlin.SynchronizedLazyImpl
Hello

- lazy mode를 이용한 lazy

  • Synchronized : 기본값이다, 초기화를 최초 호출되는 단 하나의 스레드에서만 처리하고 다른 스레드에서도 최초로 초기화 된 값을 사용한다.
  • Publication : 여러 스레드에서 동시에 호출할 수 있고, 초기화도 여러 스레드에서 진행할 수 있다. 단 다른 스레드에서 초기화를 마친 경우 초기화를 진행하지 않고 그 값을 반환한다.
  • None : 초기화가 되지 않은 경우 무조건 초기화를 진행한다. NPE를 발생할 수 있다.

코드

fun main() {
    val modeSynchronized by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
        "Synchronized"
    }
    
    val modePublication by lazy(LazyThreadSafetyMode.PUBLICATION) {
        "Publication"
    }
    
    val modeNone by lazy(LazyThreadSafetyMode.NONE) {
        "None"
    }
    
    println(modeSynchronized)
    println(modePublication)
    println(modeNone)
}

 

결과

Synchronized
Publication
None

- by 위임

하나 이상의 언터페이스를 상속받은 클래스의 인스턴스를 다른 클래스에 위임 함으로써 다른 클래스에서도 인터페이스를 사용할 수 있다.

위임을 사용하는 경우 무분별한 상속으로 야기할 수 있는 문제점(종속성)을 방지하기 위함과 표준 라이브러리의 클래스는 open으로 정의되지 않은 클래스가 많기 때문에 확장하기 위함이다.

 

코드

interface Run {
    fun run()
}

class Parent : Run {
    override fun run() {
        println("Parent Run")
    }
}

// parent의 인스턴스를 통해 Run 인터페이스가 구현된 Parent클래스를 위임 받는다.
// run메소드를 정의없이 사용할 수 있다. -> Parent의 run메소드를 가져옴
class Other(private val parent: Parent) : Run by parent {

}

fun main() {
    val other = Other(Parent())
    other.run()
}

 

결과

Parent Run

- by lazy

  • lazy로 지연초기화를 하고 by로 위임하여 인스턴스를 저장할 수 있다.
  • val로 선언된 프로퍼티에도 가능하다.
  • lazy모드에 지정된 방식을 따른다.

코드

class Base {
    val str: String by lazy {"123"}
}

fun main() {
    val base = Base()
    println(base.str)
}

 

결과

123

- by observable

값이 변경될 때 observable 콜백함수를 실행시킨다.

 

코드

import kotlin.properties.Delegates

fun main() {
    var name: String by Delegates.observable("No") {
        property, oldValue, newValue ->
        println(property)
        if(newValue == "Kang") {
            println("Hi Kang")
        }
        else {
            println("$oldValue -> $newValue")
        }
    }

    println(name)

    name = "Kang"
    println(name)

    name = "Tae"
    println(name)
}

 

결과

No
var name: kotlin.String
Hi Kang
Kang
var name: kotlin.String
Kang -> Tae
Tae

- by vetoable()

값이 변경될 때 vetoable() 콜백함수를 실행시키고 반환값이 true여야 값을 바꾼다.

 

코드

import kotlin.properties.Delegates

fun main() {
    var maxNumber: Int by Delegates.vetoable(0) {
        property, oldValue, newValue ->
        println(property)
        oldValue < newValue
    }

    maxNumber = 1
    println(maxNumber)

    // vetoable 함수의 반환값이 false이므로 변경하지 않음
    maxNumber = 0
    println(maxNumber)

    maxNumber = 3
    println(maxNumber)
}

 

결과

var maxNumber: kotlin.Int
1
var maxNumber: kotlin.Int
1
var maxNumber: kotlin.Int
3

 

Comments