수신 객체 지정 람다

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

 ``kt with``는 원래 파라미터가 2개인 함수다. 그러나 두 번째 인자인 람다를 밖으로 빼서 원래 언어가 지원하는 구문인 것 처럼 사용할 수 있다. 보기 깔끔해진다.

 ``kt with``의 람다 내에서는 전달된 객체에 prefix없이 접근할 수 있다.

```kt

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

    }

}

```

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

```kt

fun alphabet() = with(StringBuilder()) {

    for (letter in 'A'..'Z') {

        append(letter)

    }

    toString()

}

```

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

 

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

``kt apply``함수는 ``kt with``와 거의 비슷한데, 확장 함수로 정의되어 있으며 자신에게 전달된 객체를 리턴한다는 차이점이 있다.

위 함수를  ``kt apply``를 이용해 리팩토링하면 다음과 같다.

```kt

fun alphabet() = StringBuilder().apply {

    for (letter in 'A'..'Z') {

        append(letter)

    }

}.toString()

```

 

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

```kt

fun createViewWithCustomAttributes(context: Context) =

        TextView(context).apply {

            text = "Sample"

            textSize = 20.0

            setPadding(10, 0, 0, 0)

        }

```

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

```kt

fun createViewWithCustomAttributes(context: Context) {

    val t = TextView(context)

    t.text = "Sample"

    t.textSize = 20.0

    t.setPadding(10, 0, 0, 0)

    return t

}

```

 

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

``kt bar?.method()``는 메소드 호출에 사용할 수 있지만, 함수에 ``kt f(bar)``를 넘길 때는 사용할 수 없다.
이런 경우 ``kt let``을 사용할 수 있다.
```kt
getTheBestPerson()?.let { sendEmailTo(it.email) }    // 아래 구문과 동일
 
val person: Person? = getTheBestPerson()
if (person != null) sendEmailTo(person.email)
```
변수를 할당하지 않아도 된다는 장점이 있지만 ``kt let``을 중첩하면 가독성이 떨어진다는 단점이 있다.

 

run 활용

```kt
adapter ?: run { . . . }
 
if (adapter == null) { . . . }
```
 

takeif 활용

``kt takeif``는 조건이 참일 때 ``kt this``를 리턴하고, 참이 아니면 ``kt null``을 리턴한다.
```kt
// Original code
if (mBluetoothAdapter == null) { A }
if (!mBluetoothAdapter.isEnabled()) { B }
 
// Improved code
mBluetoothAdapter?.takeIf{ it.isEnabled() }?.run { B } ?: A
```
 

run let with apply also의 정의

```kt

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를 줄일 수 있음.

```kt

runCatching {

  get()

}.fold(

  onSuccess = { onSuccess(it) },

  onFailure = { onFailure(it) }

).also {

  finally()

}

```