Kotlin을 사용 할 때, 범위 지정 함수인 apply, also, let, with, run은 간결한 코드와 가독성을 위해 자주 사용하게 된다.
그러나 이 5가지 범위 지정 함수를 사용 할 때 어떤 차이점이 존재하는지 뚜렷하게 알지 못한 채 사용하게 되는 일이 많다.
알지 못하고 쓰면 모두 비슷한 기능을 하는 것 같은 5가지 범위 지정 함수의 차이와 사용 규칙을 확실히 이해하고 올바르게 사용하고자 한다.
각 함수들을 비교하기 전에, 5가지 차이점을 단박에 이해할 수 있는 좋은 표 하나를 보도록 하자.
[호출 시 수신 객체 입력]
* 명시적 전달
with가 수신 객체의 명시적 전달에 해당된다.
with의 Definitions를 보면, 매개변수를 통해 receiver: T를 명시적으로 전달함을 알 수 있다.
* 암시적 전달
apply, also, let, run이 수신 객체의 암시적 전달에 해당된다.
Definitions를 보면, T().apply와 같이 T의 확장함수를 통해 수신 객체를 암시적으로 전달함을 알 수 있다.
[코드 블록으로 수신 객체 전달]
* 명시적 전달
also, let이 코드 블록(람다)으로 수신 객체 전달의 명시적 전달에 해당된다.
Examples을 보면, 코드 블록 내에 수신 객체가 매개변수로써 명시적으로 전달되어 it을 통해 접근 하는 것을 알 수 있다.
* 암시적 전달
apply, with, run이 코드 블록(람다)으로 수신 객체 전달의 암시적 전달에 해당된다.
Examples을 보면, 코드 블록 내에 수신 객체가 T의 확장함수 형태로 암시적으로 전달되어 this를 통해 접근 하는 것을 알 수 있다.
[반환]
* 전달 받은 수신 객체
apply, also가 이에 해당된다.
Definitions를 보면, 반환 형태가 T로써 전달 받은 수신 객체를 반환함을 알 수 있다.
* 코드 블록의 수행 결과
with, let, run이 이에 해당된다.
Definitions를 보면, 반환 형태가 R로써 코드 블록의 수행 결과에 해당하는 반환 형태를 가짐을 알 수 있다.
이러한 5가지 범위 지정 함수의 차이점이 존재하기 때문에, 각 함수들은 적재적소에 알맞게 사용해야 한다.
(1) apply
코드 블록 내에서 수신 객체의 함수를 사용하지 않고 프로퍼티만 사용하여 수신 객체를 반환하는 경우 사용한다.
대표적인 예는 객체의 초기화이다.
val user = User().apply {
name = "HungSeong"
age = 24
}
(2) also
코드 블록 내에서 수신 객체를 사용하지 않거나 수신 객체의 속성을 변경하지 않고 쓸 경우 사용한다.
대표적인 예는 디버깅 시 사이드 이펙트, 값의 확인 등 객체의 유효성 검사이다.
val user = user.also {
requireNotNull(it.name)
println(it.age)
}
(3) let
수신 객체가 null이 아닐 때만 코드를 실행해야 할 경우 사용한다.
또는 지역 변수의 범위를 제한하고자 할 때 사용한다.
userInfo()?.let {
//userInfo가 non-null 일 때만 printUserAge 함수 실행
printUserAge(it.age)
}
getDatabase().let { db ->
// 변수 db의 범위가 해당 블록 내로 제한됨
}
(4) with
수신 객체가 non-null 이고, 결과를 반환 받을 필요가 없을 경우 사용한다.
val user = User("HungSeong", 24)
// user는 non-null이므로 with의 매개변수(수신 객체)로 사용 가능
with(user) {
print(user.name)
print(user.age)
}
(5) run
run 함수의 경우, 코드 블록의 마지막 수행 결과를 반환하고 코드 블록 내의 변수는 모두 임시로 사용되는 변수이므로
코드 블록 내에서 복잡한 계산을 수행 할 때, 여러 지역 변수의 범위를 제한 할 때 사용한다.
// 복잡한 계산을 run 코드 블록 내에서 수행
val userStr = run {
val name = "박훈성"
val age = 24
"저는 $name 입니다. 청춘의 나이, $age살 입니다."
}
println(userStr)
또한 객체에서 run을 호출하여 코드 블록 내에서 receiver로 수신 객체를 암시적으로 전달하기 위한 용도로도 사용한다.
// 수신 객체를 receiver로 코드 블록에 전달
val name = "ParkHunSeong"
name.run {
toLowerCase() // 암시적 전달을 통해 this 생략 가능
}
각 함수의 차이점 및 사용 용도를 확실하게 이해하여 본 함수의 목적에 맞는 클린 코드를 작성할 수 있도록 노력해야 할 것!
https://youngest-programming.tistory.com/578
'Kotlin' 카테고리의 다른 글
[Kotlin] Class, Interface 새로 배운 학습 정리 (0) | 2021.11.12 |
---|---|
[Kotlin] Type, String, Array 새로 배운 학습 정리 (0) | 2021.11.12 |
[Kotlin] 상속 변경자 open, abstract 차이 (0) | 2021.11.11 |
[Kotlin] 접근제한자, 제네릭 (0) | 2021.11.11 |
[Kotlin] 함수, 클래스, 추상화, 인터페이스 (0) | 2021.11.11 |