Post

(Kotlin) 수신 객체 지정 람다 - with / apply / let / run / takeif / also

수신 객체 지정 람다

with : prefix없이 접근하고 싶을 때

with는 원래 파라미터가 2개인 함수다. 그러나 두 번째 인자인 람다를 밖으로 빼서 원래 언어가 지원하는 구문인 것 처럼 사용할 수 있다. 보기 깔끔해진다. with의 람다 내에서는 전달된 객체에 prefix없이 접근할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
fun alphabet(): String {
val stringBuilder = StringBuilder()
return with(stringBuilder) {
for (letter in 'A'..'Z') {
this.append(letter)
append("!")    // this 생략 가능
}

this@OuterClass.somFunc()    // 바깥쪽 클래스 멤버 접근
this.toString()    // return
}
}

또는 이렇게 리팩토링 할 수 있다.

1
2
3
4
5
6
7
fun alphabet() = with(StringBuilder()) {
for (letter in 'A'..'Z') {
append(letter)
}
toString()
}

내부에서 널체크를 수행해주어야 한다는 단점이 있음. 이런 경우 let을 고려한다.

apply : 객체를 만들면서 인스턴스를 초기화하고 싶을 때

apply함수는 with와 거의 비슷한데, 확장 함수로 정의되어 있으며 자신에게 전달된 객체를 리턴한다는 차이점이 있다. 위 함수를 apply를 이용해 리팩토링하면 다음과 같다.

1
2
3
4
5
6
fun alphabet() = StringBuilder().apply {
for (letter in 'A'..'Z') {
append(letter)
}
}.toString()

apply함수는 객체의 인스턴스를 만들면서 즉시 프로퍼티 중 일부를 초기화해야 할 때 유용하다. apply 내에서 호출하는 메소드나 프로퍼티는 수신 객체의 메소드나 프로퍼티를 의미한다.

1
2
3
4
5
6
7
fun createViewWithCustomAttributes(context: Context) =
TextView(context).apply {
text = "Sample"
textSize = 20.0
setPadding(10, 0, 0, 0)
}

위 코드를 풀어 쓰면 이렇게 된다.

1
2
3
4
5
6
7
8
fun createViewWithCustomAttributes(context: Context) {
val t = TextView(context)
t.text = "Sample"
t.textSize = 20.0
t.setPadding(10, 0, 0, 0)
return t
}

let : 널이 될 수 있는 타입 인자로 넘기기

bar?.method()는 메소드 호출에 사용할 수 있지만, 함수에 f(bar)를 넘길 때는 사용할 수 없다. 이런 경우 let을 사용할 수 있다.

1
2
3
4
getTheBestPerson()?.let { sendEmailTo(it.email) }    // 아래 구문과 동일
val person: Person? = getTheBestPerson()
if (person != null) sendEmailTo(person.email)

변수를 할당하지 않아도 된다는 장점이 있지만 let을 중첩하면 가독성이 떨어진다는 단점이 있다.

run 활용
1
2
3
adapter ?: run { . . . }
if (adapter == null) { . . . }

takeif 활용

takeif는 조건이 참일 때 this를 리턴하고, 참이 아니면 null을 리턴한다.

1
2
3
4
5
6
7
// Original code
if (mBluetoothAdapter == null) { A }
if (!mBluetoothAdapter.isEnabled()) { B }

// Improved code
mBluetoothAdapter?.takeIf{ it.isEnabled() }?.run { B } ?: A

run let with apply also의 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
inline fun <T, R> T.run(block: T.() -> R): R = block()
inline fun <T, R> T.let(block: (T)  -> R): R = block(this)
inline fun <T, R> T.with(receiver: T, block: T.() -> R): R = receiver.block()
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}


use { } open하는 자원에 연결  close() 자동으로 호출해줌
try-with-resources  대응한다고 보면 된다.

runCatching

try-catch-finally 대신 runCatching 도 사용 가능함. catch문 결과를 반환해주기 때문에 indent depth를 줄일 수 있음.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
runCatching
{

get
()

}
.
fold
(

onSuccess
=
{
onSuccess
(
it
)
}
,

onFailure
=
{
onFailure
(
it
)
}

)
.
also
{

finally
()

}

This post is licensed under CC BY 4.0 by the author.