Kotlin by lazy VS lateinit var 차이점과 사용법 완벽 가이드

개요

Kotlin은 초기화 지연(Delayed Initialization)을 지원하기 위해 두 가지 강력한 키워드인 by lazylateinit var를 제공합니다. 이 두 가지는 초기화를 지연시켜 메모리 효율성을 높이고, 복잡한 초기화 로직을 간결하게 처리할 수 있도록 돕습니다.

이번 포스트에서는 by lazy와 lateinit var의 동작 원리, 차이점, 그리고 실용적인 사용 예제를 소개합니다.


1. by lazy

by lazy는 읽기 전용 프로퍼티의 초기화를 지연(Lazy Initialization)시키는 데 사용됩니다.

1.1 기본문법

val propertyName: Type by lazy {
  // 초기화 코드
}

1.2 동작 원리


2. lateinit var

lateinit var는 읽기/쓰기 가능한 프로퍼티의 초기화를 나중에 설정할 수 있도록 합니다.

2.1 기본문법

lateinit var propertyName: Type

2.2 동작 원리


3. “by lazy 와 lateinit var” 차이점

특징 by lazy lateinit var
사용 대상 val (읽기 전용 프로퍼티) var (읽기/쓰기 가능 프로퍼티)
초기화 시점 첫 호출 시 자동 초기화 명시적으로 초기화 필요
스레드 안전성 기본적으로 스레드 안전(Thread-Safe) 스레드 안전 보장 안 됨
초기화 여부 확인 불가능 ::propertyName.isInitialized
에러 발생 여부 호출 전 초기화 코드는 실행되지 않음 초기화되지 않은 상태에서 접근 시 예외 발생
초기화 대상 객체 또는 복잡한 연산 결과 외부에서 값을 할당해야 하는 프로퍼티

4. 실용적인 사용 예제

4.1 by lazy 사용 예제: 무거운 연산 결과 지연 초기화

val databaseConnection: Database by lazy {
  println("Initializing database connection...")
  Database.connect("url", "username", "password")
}

fun main() {
  println("Before accessing databaseConnection")
  println(databaseConnection) // 여기서 초기화 발생
  println(databaseConnection) // 재사용
}
/*** 출력
 * Before accessing databaseConnection
 * Initializing database connection...
 * Database(url, username)
 * Database(url, username)
 */

활용 상황

4.2 lateinit var 사용 예제: 의존성 주입(DI)

class MainActivity : AppCompatActivity() {
  lateinit var binding: ActivityMainBinding

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    binding.textView.text = "Hello, Kotlin!"
  }
}

활용 상황

4.3 초기화 여부 확인 (lateinit)

class MyClass {
    lateinit var name: String

    fun printName() {
        if (::name.isInitialized) {
            println("Name is $name")
        } else {
            println("Name is not initialized")
        }
    }
}

fun main() {
    val obj = MyClass()
    obj.printName() // Name is not initialized
    obj.name = "Kotlin"
    obj.printName() // Name is Kotlin
}

4.4 추가 활용 사례

class UserTest {
    private lateinit var user: User

    @Before
    fun setUp() {
        user = User("Alice", 25)
    }

    @Test
    fun testUserName() {
        assertEquals("Alice", user.name)
    }
}


5. 잘못된 사용 사례

5.1 by lazy 잘못된 사용

val randomValue: Int by lazy { (1..100).random() }
println(randomValue) // 항상 같은 값 반환

5.2 lateinit의 잘못된 사용

lateinit var age: Int // 컴파일 에러
var age: Int = 0

6. 결론: 언제 무엇을 사용할까?

상황 적합한 선택
읽기 전용 값 by lazy
초기화 비용이 큰 작업 by lazy
라이프사이클 후에 초기화해야 하는 값 lateinit var
외부에서 값을 할당해야 하는 경우 lateinit var
스레드 안전성이 필요한 경우 by lazy
초기화 여부를 동적으로 확인해야 하는 경우 lateinit var



Related Posts