코틀린을 처음 접했을 때, 정확한 가이드라인이나 기준이 없어서 당황스러웠던 것이 바로 범위 지정 함수였습니다.
아마 자바에서 코틀린으로 넘어오신 수많은 안드로이드 개발자분들이 그러셨으리라 추측합니다.
저 또한 여전히 그러므로, 직접 몇 가지 스니펫을 기록해두려 합니다.
run
1. BufferedReader 등으로 유저의 입력을 받는 경우 (보통 PS) 전역 변수에 값을 재할당하고자 할 때 사용합니다.
bufferedReader.readLine().split(" ").map { it.toInt() }.run {
n = this[0]
m = this[1]
}
/*
물론 다음과 같이 할 수 있겠지만, 코드가 지저분해져 저는 선호하지 않습니다.
*/
val (a, b) = bufferedReader.readLine().split(" ").map { it.toInt() }
n = a
m = b
with
1. Databinding 을 이용한다면 보통 해당 Activity / Fragment / Adapter 에서 binding 이라는 변수명으로 Databinding 을 활용하게 되는데, 이때 다음과 같이 사용합니다.
with(binding) {
tvAdTitle.text = item.adTitle
tvAdBody.text = item.adBody
}
let
1. Nullable 을 핸들링해야 하는 경우에 사용합니다. 비동기 통신 등을 통해 할당될 값이 통신의 실패로 말미암아 로드되지 않은 경우, 무작정 표기하려 할 경우 문제가 생길 수 있습니다. 그럴 경우, 다음과 같이 사용합니다.
text?.let {
println(it)
}
text 의 값이 null 인 경우, let 스코프를 건너뜁니다.
🤔 만약 null 일 때의 처리가 필요하다면요? TextView 에 표시해줘야 하지 않을까요?
text 가 없는 경우, 다음과 같은 함수를 만들 수 있겠습니다.
fun isEmptyText(text: String?) = text == null
간단한 함수고, 직관적인 함수라 깔끔하긴 합니다만... 굳이 함수까지 만들 필요가 없습니다.
text?.let {
println(it)
} ?: run {
println("No Text To Print")
}
엘비스 연산자와 run 람다를 이용하면 해당 코드 블록의 연장선 내에 해결이 가능합니다.
물론 위 스니펫의 코드는 더 간단하게 처리할 수 있지만, let 과 run 의 활용을 설명하기 위해 작성하였습니다.
apply
1. apply 는 기본적으로 수신 객체의 프로퍼티만을 사용하도록 합니다. 보통 객체 초기화시에 사용합니다.
val pieChart = binding.pieChart.apply {
xAxisMaximum = 100
xAxisMnimum = 0
}
2. apply 와 also는 다른 스코프들과 다르게 블록의 결과를 리턴하는 것이 아니라 수신 받은 객체를 그대로 리턴합니다.
이러한 속성으로 인해 필자는 apply 와 aslo 를 두고 어떤 것을 사용해야하나 고민했던 상황이 있습니다.
list 나 map 등 컬렉션에서의 경우인데, 다음과 같습니다.
코드1
fun getUseList(user1: User, user2: User, user3: User) = ArrayList<User>().apply {
add(user1)
add(user2)
add(user3)
}
물론 이 상황에서
코드2
fun getUseList(user1: User, user2: User, user3: User) {
val list = ArrayList<User>().run {
add(user1)
add(user2)
add(user3)
}
return list
}
이런 식으로 작성해도 좋지만, 코드1 처럼 함수를 작성하면서 리턴값을 바로 설정하고 싶었습니다. 코틀린이 제공하는 해당 기능이 정말 편하고, 가독성도 훨씬 좋습니다. 저는 이 기능을 활용하면서 스코프 함수까지 쓰고 싶었습니다. 그래서 그냥 평소처럼 초기화한다는 마음으로 apply 를 사용하려 하였으나...
코틀린에서는 apply 내에서는 수신 객체의 함수를 사용하지 않도록 권고하고 있습니다.
그래서 apply 대신 also 를 사용하려 했지만, also 는 객체 내 데이터의 유효성 검증이나 프로퍼티를 활용한 디버깅 및 로깅 등의 부가적 목적에 사용하는 것이 옳다는 이야기가 많았습니다.
관련된 레퍼런스를 찾기 위해 코틀린 공식 문서를 참고해보니, 다음과 같은 예시 코드가 있었습니다.
그리하여,
fun getUseList(user1: User, user2: User, user3: User) = ArrayList<User>().apply {
add(user1)
add(user2)
add(user3)
}
스코프 함수도 결국엔 생산성을 위해 만들어진 기능이기 때문에, 너무 강박을 갖고 적용하려 들지 않고 유연한 자세를 취하는 것이 좋다는 생각이 듭니다. 수신 객체를 그대로 반환해야하는 상황에서의 초기화는 apply, 그렇지 않은 상황에서는 run 또는 also 를 사용하는 것이 좋을 듯합니다.
also
1. also 는 let 과 같이 객체가 람다에 암시적으로 전달됩니다.
프로퍼티에 대한 변경 없이 해당 객체를 그대로 사용하는 경우에 사용하면 됩니다.
프로퍼티에 대한 변경이 필요하다면 also 보단 apply 를 사용하면 좋을 것 같습니다.
fetchedData.also {
Log.d("Debugging", it.toString())
}
이런식으로 디버깅에 활용할 수도 있고,
imageManager.also {
showSplashImage(it)
cacheToDisk(it)
}
함수 호출에도 활용할 수 있습니다.
'Kotlin' 카테고리의 다른 글
ChannelFlow, CallbackFlow 제대로 알기 (0) | 2022.11.03 |
---|---|
Channel 제대로 알기 (0) | 2022.11.02 |
StateFlow, SharedFlow 제대로 알기 (0) | 2022.11.01 |
Sealed Class, Enum 제대로 알기 (0) | 2022.10.28 |
out, in 제대로 알기 (0) | 2022.10.26 |