let 으로 if (item != null) 을 대신할 수 있을까?

Links: https://medium.com/mobile-app-development-publication/kotlin-dont-just-use-let-7e91f544e27f
Last Edited: April 2, 2021 1:34 AM

https://kotlinlang.org/docs/null-safety.html#checking-for-null-in-conditions

To perform a certain operation only for non-null values, you can use the safe call operator together with let:

val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
    item?.let { println(it) } // prints Kotlin and ignores null
}
if (item != null) { /* Do something */}

-> item?.let { /* Do something */}

1. 추천하지 않는 케이스

1) immutable variable

NOT RECOMMEND

fun process(str: String?) { str?.let { println(it) } }

Decompile

public static final void process(@Nullable String str) {
      if (str != null) {
         **boolean var2 = false;**
         System.out.println(str);
      }
}

process function 을 많이 사용하면 이득이 없는 필요없는 코드를 생산

RECOMMEND

str 은 이미 if 시점에 nullable 이 아닌 것으로 자동 캐스팅 된다.

// RECOMMENDED
fun process(str: String?) {
    if (str != null) {
        // Do Something
    }
}

2) 내부 변수 값 세팅

NOT RECOMMEND

webviewSetting?.let {
    it.javaScriptEnabled = true
    it.databaseEnabled = true
}

RECOMMEND

webviewSetting?.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

run 을 사용하면 this 가 scope로 잡히기 때문에 it 을 걷어낼 수 있다.

3) 변수에 let 을 사용하고 나서, 그 변수 값에다 chaining 걸고 싶을 때

NOT RECOMMEND

stringList?.let {
    println("Total Count: ${it.size}")
    it // <- 값 반환을 위한 목적
}?.forEach{ println(it) }

추천하지는 않지만 좀 더 나은 모양

stringList?.also {
    println("Total Count: ${it.size}")
}?.forEach{ println(it) }

2. LET 사용을 추천하는 케이스

1) mutable variable 의 null을 체크하는 경우 let 을 사용하면 scope 내에서 immutable 보장

RECOMMEND

//전역변수라서 값이 바뀔 수 있음
private var str: String? = null

fun process() {
    str?.let { /*Do something*/ }
}

NOT RECOMMEND

if 로 체크해도 변수의 상태나 내용이 바뀔 수 있는 경우

private var str: String? = null

fun process() {
    if (str != null) {
        println(**str?**.length)
    }
}

2) 외부에 있는 변수와 구분

RECOMMEND

var javaScriptEnabled = false
var databaseEnabled = false

webviewSetting?.let {
    javaScriptEnabled = it.javaScriptEnabled
    databaseEnabled = it.databaseEnabled
}

NOT RECOMMEND

this 의 어디에 속하는 scope 인지 알기 어려움

var javaScriptEnabled = false
var databaseEnabled = false

webviewSetting?.run {
    javaScriptEnabled = this.javaScriptEnabled
    databaseEnabled = this.databaseEnabled
}

3) long nullable chain

RECOMMEND

실제 nullable 한 곳을 let으로 끊어보자

fun process(string: String?): List<Char>? {
    return string?.let {
        it.asIterable().distinct().sorted()
    }
}

NOT RECOMMEND

디컴파일 해보면 복잡도가 올라가는 것을 볼 수 있다.

fun process(string: String?): List<Char>? {
    return string?.asIterable()?.distinct()?.sorted()
}