Uknow's Lab.
article thumbnail

 

 

1. 코틀린에서 전역변수 사용

<kotlin />
class Person { String name; } public class Main { static Person person; public static void main(String[] args) { person = new Person(); } }

 

자바 개발자라면 클래스 내 변수를 어느 메소드에서 자유롭게 접근하도록 하기 위해

전역변수로 선언만 해둔 후, 이를 특정 시점에 초기화 및 생성시켜 써본 경험이 한 번쯤은 있으실 겁니다.

 

 

<kotlin />
class Person val person:Person // 초기화하지 않으면 에러 발생!! fun main() { val person = Person() }

하지만 코틀린에서는 위와 같은 코드는 성립이 안됩니다.

코틀린에서는 변수의 선언과 동시에 초기화하도록 권장하기 때문입니다.

 

 

<kotlin />
class Person var person:Person? = null // null로 초기화 하여 에러는 나지 않지만, 권장하진 않음 fun main() { val person = Person() }

물론, ? 키워드를 붙여 nullable 타입으로 만든 뒤, null로 초기화하여 사용할 수 있으나,

null로 초기화하는 것은 코틀린에서 그닥 권장하는 사용법은 아닙니다.

 

 

2. lateinit을 사용한 늦은 초기화

<kotlin />
class Person lateinit var person: Person // lateinit을 붙여 늦게 초기화해준다고 명시 lateinit val person2: Person // 에러발생 !! val에는 사용 불가능 fun main() { val person = Person() }

 

이럴때 사용할 수 있는게 lateinit 키워드입니다.

키워드 이름에서 알 수 있듯, 늦게 초기화해준다는 의미입니다.

 

이는 val에는 사용 불가하며 오직 var에만 사용 가능합니다.

또한, Int, Long, Double, Char와 같은 primmitive 타입에는 사용이 불가합니다.

뭐... int 같은 경우는 어차피 자바에서도 별 값을 주지 않으면 0으로 초기화되니,

var n = 0 과 같이 그냥 초기화하는게 좋을 것 같네요.

 

 

2.1. 초기화 했는지는 어떻게 확인해?

<kotlin />
class Person lateinit var person: Person // lateinit을 붙여 늦게 초기화해준다고 명시 fun main() { if (::person.isInitialized) { // 초기화가 되었는지 확인 println("Initialized") } else { println("Not initialized") } } // 출력 >> Not initialized

 

초기화 여부는 ::(객체).isInitialized 를 통해 확인할 수 있습니다.

앞에 꼭 ::를 붙여야 확인할 수 있습니다.

 

 

3. 초기화 지연 - lazy

<kotlin />
class Person { init { println("Person created") } fun sayHello() { println("Hello") } } val person by lazy { Person() } // 초기화를 지연시킴 fun main() { println("Point1") person.sayHello() // 처음 사용되는 시점에 초기화 println("Point2") } // 출력 >> Point1 Person created Hello Point2

 

lazy는 지연 초기화를 사용할 때 쓰는 키워드로, 오직 val만 가능합니다.

이는 말 그대로 초기화를 지연시키는 역할을 하는데요.

by lazy { }와 같이, 람다식을 이용해 사용합니다.

 

위 예시 코드에서, Person 클래스는 생성되는 순간 init에서 println(”Person created”)를 출력합니다.

그리고, 전역변수로써, person을 선언했고, lazy의 바디부분에 생성 코드를 삽입했습니다.

 

이후, main()에서 person 객체를 처음 사용하는 순간 lazy 바디 내 코드가 실행됩니다.

 

 

<kotlin />
class Person { init { println("Person created") } fun sayHello() { println("Hello") } } val person by lazy { // lazy 바디 내 코드가 실행됨. val a = 10 val b = 100 println(a + b) println("코드 실행") Person() // 제일 마지막 줄이 person에 대입 } fun main() { println("Point1") person.sayHello() // 처음 사용되는 시점에 초기화 println("Point2") } >>> Point1 110 코드 실행 Person created Hello Point2

 

참고로, lazy의 바디 부분은 람다식으로써,

person.sayHell()를 통해 person 객체가 처음 호출되는 순간,

lazy의 바디 내 코드가 순차적으로 실행되며,

제일 마지막줄만 person에 대입됩니다.

 

lazy 키워드를 잘 활용하면 메모리와 시간 측면에서 더 효율적으로 작동할 수 있습니다.

 

 

4. 부록. lazy를 유용하게 써본 개인적 경험

사실 저는 lazy 키워드를 메모리 측면보다는, 프로그램 흐름을 맞추는데 유용하게 썼는데요.

 

<kotlin />
private val chatAdapter = ChatAdapter(applicationContext, chatModels) // 엥 applicationContext 없는데요. 앱 팅김!!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) binding.rvChat = chatAdapter }

 

리사이클러뷰 등을 사용할 땐, 저는 주로 Adapter를 전역변수로 두는 편입니다.

Adapter에 굳이 context를 건네줄 필요는 없지만,

어댑터에서 특정 작업을 할 때 context가 필요한 경우가 있어 종종 넘겨주곤 합니다.

 

하지만 applicationContext는 onCreate()가 호출될때 생성되며,

전역변수로 선언+초기화를 할 경우,

onCreate()보다 먼저 생성되어 context를 가져오지 못해 앱이 팅기곤 했습니다.

 

 

<kotlin />
private lateinit chatAdapter:ChatAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) // 전역변수로 선언만 해두고, 생성은 onCreate() 이후에 해야함. chatAdapter = ChatAdapter(applicationContext, chatModels) binding.rvChat = chatAdapter }

 

따라서, 해당 예제에서 context를 넘겨주고 싶을 땐, 전역변수로 선언만 해두고,

onCreate() 이후 생성하는 코드를 따로 넣어줘야 했습니다.

 

 

<kotlin />
private val chatAdapter by lazy { ChatAdapter(applicationContext, chatModels) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) binding.rvChat = chatAdapter }

 

하지만 lazy 키워드를 사용하여, 선언과 초기화를 동시에 처리할 수 있었습니다.

(실제로 초기화가 된 것은 아닙니다)

 

안드로이드에서 거의 모든 코드는 onCreate() 이후 작동합니다.

따라서, lazy를 사용하면 자연스럽게 context를 onCreate() 이후에 넘겨줄 수 있게 되었습니다.

 

다시 한 번 말씀드리지만… 리사이클러뷰 어댑터에 굳이 context를 넘겨줄 필요는 없습니다.

액티비티보단 리사이클러뷰에서 처리하는게 더 나을 것 같은 작업이 있어 context를 넘겨줬을 뿐…

profile

Uknow's Lab.

@유노 Uknow

인생은 Byte와 Double 사이 Char다. 아무말이나 해봤습니다.