Kotlin Standard Function



reference: 

https://android.jlelse.eu/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84





Scoping function?

Kotlin에서 제공하는 standard library 중 scoping functions에 대해 소개합니다.



Simple example of "run"

fun test() {
var mood = "I am sad"

run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}


위 예제와 같이 사용한다면 분명 일반적인 scope의 역할 말고는 별다른 게 없어 보입니다. mood라는 같은 이름의 변수를 scope 별로 달리 사용할 수 있습니다. 하지만 아래의 예제는 조금 특별해 보입니다.

run {
if (firstTimeView) introView else normalView
}.show()


show()?? 갑자기 메소드를 호출했습니다. 이는 scope가 어떤 일련의 반환값을 갖는 메소드처럼 동작하는 것인데, 

return을 통하여 함수를 반환하는 것이 아니라 마지막 줄이 어떤 값을 반환하게 됩니다. 

따라서 위의 예제는 introView 혹은 normalView의 show()를 호출하는 것 입니다.



"with" & "T.run"

with(webview.settings) {
javaScriptEnabled = true
databaseEnabled = true
}
// similarly
webview.settings.run {
javaScriptEnabled = true
databaseEnabled = true
}


어떤 객체를 주체로 동작을 합니다. 

위 두 예제는 webview.settings를 주체로 어떠한 동작을 하는 것인데, 물론 scope안의 값들은 그 주체의 field입니다.

아래는 직접 실험해 본 소스입니다.


class ExampleUnitTest {
class AClass {
var foo = 1
}

@Test
fun test() {
val a = AClass()
val foo2 = a.run {
foo = 2
}
println("foo in a : ${a.foo}\nfoo2 : $foo2")
}
}
foo in a : 2
foo2 : kotlin.Unit

AClass 객체의 field를 T.run을 통해 조작한 후, 새로운 변수 foo2에 assign 하려고 했습니다. 

기본적으로 Kotlin에서는 assignment를 assignment로 사용할 수 없다는 걸 다시 깨달았죠...


아래 예제는 null check가 활용되는 예입니다. 아무래도 T?.run이 좀더 깔끔해보입니다. 

당연히, T가 null이면 run은 실행되지 않습니다.

// Yack!
with(webview.settings) {
this?.
javaScriptEnabled = true
this?.databaseEnabled = true
}
}
// Nice.
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}



"let"


run이 기본적으로 this의 field나 method를 this가 생략된 형태로 접근하게 해주었으나, let에서는 lambda처럼 전달합니다. 

T.let에서 it는 T의 this로 쓰입니다. 


run : this as parameter

let : it as parameter

stringVariable?.run {
println("The length of this String is $length")
}
// Similarly.

stringVariable?.let {
println("The length of this String is ${it.length}")
}



"also"


also는 의미 그대로 이것 또한 해보라는 의미로 보입니다. 이는 standard function의 chain을 보면 확연히 알 수 있습니다.

let은 manipulated된 그대로 반환하여 reassign 하여 재사용하는 반면, 

also안의 it는 scope안에서 새롭게 생성된 같은 값의 temp 변수라고 볼 수 있겠습니다.

val original = "abc"
// Evolve the value and send to the next chain
original.let {
println("The original String is $it") // "abc"
it.reversed() // evolve it as parameter to send to next let
}.let {
println("The reverse String is $it") // "cba"
it.length // can be evolve to other type
}.let {
println("The length of the String is $it") // 3
}
// Wrong
// Same value is sent in the chain (printed answer is wrong)
original.also {
println("The original String is $it") // "abc"
it.reversed() // even if we evolve it, it is useless
}.also {
println("The reverse String is ${it}") // "abc"
it.length // even if we evolve it, it is useless
}.also {
println("The length of the String is ${it}") // "abc"
}
// Corrected for also (i.e. manipulate as original string
// Same value is sent in the chain
original.also {
println("The original String is $it") // "abc"
}.also {
println("The reverse String is ${it.reversed()}") // "cba"
}.also {
println("The length of the String is ${it.length}") // 3
}


"apply"


T.apply는 run과 비슷하지만 기본적으로 T의 this를 반환합니다. 아래 예제와 같이 객체 초기화에 간결히 사용될 수 있겠습니다.

// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }



Function selection


무엇을 사용해야 할지 모르겠다면 아래 그림을 참고하면 됩니다. 음... 너무 많은데..?

익숙해진다면 멋있게 쓸 수 있을 것 같은 기능들이네요.


'programming > android' 카테고리의 다른 글

기본적인 메모리 관리  (0) 2018.03.06
Splash 화면 구성하기  (3) 2018.02.20
Kotlin Coroutines #2  (0) 2018.01.28
Kotlin Coroutines #1  (1) 2018.01.27
Tools로 xml layoyt의 preview 제대로 표시하기  (0) 2018.01.22

+ Recent posts