코틀린 고급편 강의 수강 후 정리한 글입니다.
lateinit
인스턴스화 시점과 프로퍼티 초기화 시점을 분리하고 싶을 때 사용한다.
인스턴스화를 한 번만 하고, 테스트를 할 때 변수를 초기화하고 싶은데, 인스턴스화 할 때, 초기값을 넣어주고 싶지 않을 때. lateinit 을 사용하지 않는다면 다음과 같은 방법을 고려해볼 수 있다.
방법 1. 기본 값을 넣어주기
변수가 변경되어야 하니, val 대신 var 이 필요하고, 기본값이 있으니 생성자에 있을 필요가 없다.
하지만 위험한 방법이다. name 을 초기화 하지 않더라도, 예외가 발생하지 않기 때문이다.
"Hong" 이라는 이름이 실제로 존재하는 이름일 수 도 있다. 이런 문제를 해결하기 위한 방법 -> nullable 로 만들어보는 것이다.
class PersonTest1 {
// 기본값을 정의했기 때문에
private val person = Person()
@Test
fun isKimTest() {
// given
val person = person.apply { name = "김테스트"}
// when & then
assertThat(person.isKim).isTrue
}
@Test
fun maskingNameTest() {
// given
val person = person.apply { name = "웨이브"}
// when & then
assertThat(person.maskingName).isEqualTo("웨**")
}
}
class Person(
var name:String = "Hong"
) {
val isKim: Boolean
get() = this.name.startWith("김")
val maskingName: String
get() = name[0] + (1 until name.length).joinToString
}
방법 2. nullable 로 이름을 만들자
실제 null 이 될 수 없지만, nullable 로 만드는 것이다. null 이 될 수 없기에 계속 null 처리가 들어가서 ?. ?: !! 연산들이 따라가서 불편하다는 점이 있다. 때문에 코틀린에서는 lateinit 을 사용한다.
class Person {
var name: String? = null
val isKim: Boolean
get() = this.name!!startWith("김")
val maskingName: String
get() = name!![0] + (1 until name.length).joinToString
}
방법 3. lateinit 을 사용한다.
초기값을 지정하지 않고도, null 이 들어가지 않는 변수를 선언할 수 있다. 하지만, 여전히 초기화 되지 않은 상태에서 접근하면, 막아주는 기능을 제공하고 있다. lateinit 은 컴파일 단계에서 nullable 로 바꿔준다.
private lateinit var name: String
fun main() {
val p = Person() // error 발생
p.isKim
}
하지만, lateinit 에도 한계는 존재하는데, primitive type 에는 사용이 불가능하다.
lazy
변수를 초기화할 때, 지정된 로직을 1회만 호출하고 싶은 경우가 있다. 다음과 같은 코드들을 쓰게 된다면, name 을 쓸 때마다 sleep 이 호출된다.
방법 1. getter 안에 초기화 로직을 넣기
class Person {
val name:String
get() {
Thread.sleep(2_000)
return "김테스트"
}
}
방법 2. init 블록에서 초기화 로직을 넣기
class Person {
val name:String
init {
Thread.sleep(2_000)
name = "김테스트"
}
}
방법 3. backing property 를 사용하기
이걸 구현하려면 backing property 를 사용해야 한다. 이렇게 하면, cost 비용이 높은 Thread.sleep 호출이 매번 호출되지는 않을 것이다.
class Person2 {
prviate var _name: String = null
val name: String
get() {
if ( _name == null) {
Thread.sleep(2_000L)
this._name = "김초기화값"
}
return this._name!!
}
}
근데 이렇게 매번 길게 작성하는 건 상당히 번거롭다... 이럴 때, by lazy 를 사용하면 된다. backing property 도 필요가 없다.
최초 1회만 실행되고, Thread Safe 한 구조이다.
방법 4. by lazy 키워드 사용
class Person {
val name: String by lazy {
Thread.sleep(2_000)
"김테스트"
}
}
정리
lateinit
: 초기화를 지연시킨 변수. 초기화 로직이 여러 곳에 위치할 수 있다. 초기화 없이 호출하면 예외가 발생한다.
lazy
: 초기화를 get 호출 전으로 지연시킨 변수이다. 초기화 로직은 변수 선언과 동시에 한 곳에만 위치할 수 있다.
'[ Kotlin ]' 카테고리의 다른 글
코틀린의 제네릭 제약과 제너릭 함수 (0) | 2024.08.29 |
---|---|
코틀린의 선언 지점 변성/ 사용 지점 변성 (0) | 2024.08.29 |
코틀린의 배열과 리스트, 제너릭과 무공변, 공변, 반공변 (1) | 2024.08.29 |
코틀린의 제너릭과 타입 파라미터 (0) | 2024.08.29 |
코틀린의 scope function - 20강 (0) | 2024.08.29 |