안녕하세요 :)
오늘은 코틀린의 다섯 가지 함수를 비교해보고자 합니다.
[2022.07.14 업데이트]
SCOPE 함수란?
객체의 컨텍스트 내에서 코드 블록을 실행하는 것이 유일한 목적인 함수입니다.
람다식으로 SCOPE 함수를 호출할 때 임시적으로 범위를 생성하고, 해당 범위 내에선 객체의 이름 없이 객체에 접근할 수 있습니다(it, this 등)
기본적으로 5가지 함수는 같은 기능을 하지만 표현식이 어떻게 되는지, 블록 내에서 객체 사용법에 따라 구분합니다
📍 let
함수원형
public inline fun <T,R> T.let(block : (T) -> R): R
객체를 블록의 인자(T)로 넘기고, 람다의 결과값(R)을 반환합니다.
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
}
또 let은 safe call operator (?.) 를 사용해서 null체크를 할 수 있습니다.
그러므로, 아래와 같은 경우에 쓰면 좋습니다
📌 non-null인 코드 실행하는 경우
📌 단일 지역변수 범위를 제한하는 경우
val phone = Phone("", "")
val result = phone?.let { it ->
it.number="999"
it.company="samsung"
it
}
📍 also
함수원형
public inline fun <T> T.also(block: (T) -> Unit): T
객체를 블록의 인자(T)로 넘기고 받아온 객체 그대로(T)를 return 합니다.
객체말고 다른 값을 반환해야 하는 경우엔 also를 사용하는 것이 적합하지 않습니다
그러므로, 아래와 같은 경우에 쓰면 좋습니다
📌 람다가 객체를 사용하지 않거나 속성을 변경하지 않는 경우
📌 디버그 , Log, 데이터 유효성 확인에 적합
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
📍 apply
함수원형
public inline fun <T> T.apply(block: T.() -> Unit): T
block에 람다 리시버(T.())를 전달하고 자기자신(T)을 반환합니다
람다 리시버로 전달받았기 때문에 객체의 property에 접근할 때 it, this를 사용하지 않아도 됩니다
주로 객체 초기화에 많이 쓰입니다.
그러므로, 아래와 같은 경우에 쓰면 좋습니다
📌 객체 함수를 사용하지 않고 객체를 다시 반환하는 경우
📌 객체의 속성을 구성할 때
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
📍 With
함수원형
public inline fun <T, R> with(receiver: T, block: T.() -> R): R
객체를 인수로 받고(T), block에 람다 리시버(T.())를 전달하고 람다결과(R)를 반환합니다
null에 대해 별도로 처리를 해야 하기때문에 null이 아닌 경우에 사용하는 것이 좋습니다
📌 non-null 객체를 사용하고 block의 return 값이 필요하지 않을 때 사용
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
📌 객체에서 여러 개의 메소드를 호출할 때
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) {
penDown()
for (i in 1..4) {
forward(100.0)
turn(90.0)
}
}
📍 run
run은 특이하게 원형이 2개가 있습니다
함수원형 ① : 확장함수도 아니고 block에 입력값도 없는 경우
public inline fun <R> run(block: () -> R): R
📌 객체를 생성하기 위해서 block안에 묶어서 가독성을 높이는 경우
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
함수원형 ② : 람다 리시버를 인자로 받고 람다 결과를 반환
public inline fun <T, R> T.run(block: T.() -> R): R
📌 값을 계산하거나, 여러 변수의 범위 제한 시 사용
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
코틀린을 사용하면서 자주 사용하지만 헷갈리는 Scope 함수에 대해 정리해보았습니다
헷갈리는 개념이지만 Scope 함수들을 용도에 따라 사용하면 소스 가독성이 높아집니다
포스팅 속 예제는 코틀린 공식 문서에 있는 소스입니다
궁금하신 점이나 의견이 있으시면 댓글 부탁드립니다 감사합니다 😊
'💡 Kotlin' 카테고리의 다른 글
[Kotlin] Collection (List, Set, Map) (0) | 2023.01.03 |
---|---|
[kotlin] Pair (0) | 2023.01.02 |
[Kotlin] 배열 (0) | 2022.07.02 |
[Kotlin/Android] Room 사용하기 (0) | 2022.06.30 |
[Kotlin] 동적 View 생성 (0) | 2021.06.13 |