Android/App개발2020. 2. 12. 14:45

https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84

 

코틀린 표준 함수들 일부는 어디에 사용하는지 명확하게 알지 못한다. 이에 대해 그 차이점을 명확하게 설명하고 어떤걸 사용해야 하는지 소개하고자 한다.

범위함수(Scoping functions)

run, with, T.run, T.let, T.also 그리고 T.apply에 대해 집중할거다. 

다음은 run함수의 범위지정기능을 설명하는 간단한 코드이다.

불러오는 중입니다...

 

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

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

  println(mood) // I am sad
}



test함수 내의 분리된 범위에서 mood는 재정의 되고 "I am happy"가 프린트된다. run 범위내에 제한된다.
이게 범위지정함수인데 별로 유용해보이지 않지만 범위지정으로써의 장점이 있다. 먼가를 반환할 수 있다.

아래코드를 보면 show()를 두번 호출하지 않고 두개의 view에 적용이 가능하다.

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



범위지정함수의 3가지 속성

범위지정함수를 좀더 흥미있게 만들기 위해 3가지 속성으로 나누고 이를 통해 각각을 구별할 수 있다.

1. Normal vs. extension function

with, T.run 은 상당히 유사하다.

with(webview.settings) {
  javascriptEnabled = true
  databaseEnabled = true
}

webview.settings.run {
  javascriptEnabled = true
  databaseEnabled = true 
}



두가지 차이점은 일반함수냐 확장함수냐이다. 그래서 각각의 장점은 멀까?

webview.settings가 null일 수 있다고 생각해보자

// 별로다
with(webview.settings) {
  this?.javascriptEnabled = true
  this?.databaseEnabled = true
}

// 이게 더 좋아
webview.settings?.run {
  javascriptEnabled = true
  databaseEnabled = true
}


이 경우는 T.run 사용이 더 유용하다.


2. This vs. it 인자

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

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


T.run의 경우 block: T.()의 형태를 가지며 따라서 범위내에서 this로 참조가 가능하다. this의 경우 일반적으로 생략이 가능하므로 $this.length가 아니라 $length가 가능하다.

T.let의 경우 block: (T)의 형태를 가지며 인자를 따로 명시하지 않으면 it으로 접근이 가능하다.


위 예를 보면 T.run이 좀더 암시적이어서 우세해 보이나 T.let의 장점도 존재한다.

* let은 함수/멤버 변수를 외부 클래스 함수/멤버와 구별하기가 더 좋다.
* 파라메터로 this가 전달되면 생략이 불가한데 it이 더 짧고 명확하다
* let은 변수명을 it이 아니라 다른 이름으로 부여가 가능하다.

value?.let { myname -> 
  println("The name is $myname");
}


3. this 또는 다른 타입의 반환

value?.let {
  println("The length of val is ${it.length}")
}

value?.also {
  println("The length of val is ${it.length}")
}


두가지의 차이점은 let은 임의 타입을 반환하고 also는 동일한 this타입을 반환한다.
둘다 체이닝함수에 유용한다. T.let은 연산된 결과를 다음 체인에 전달하고 T.also는 동일한 객체를 전달한다.

val original = "abc"

original.let {
  prinln("The original string is $it") // 원래 문자열
  it.reversed() // 리버스된 문자열 반환
}.let {
  println("Ths reversed string is $it") 
  it.length. // 문자열길이 반환
}.let {
  println("The length is $it") // 전달받은 타입은 숫자
}

original.let {
  prinln("The original string is $it")
  it.reversed()
}.also {
  println("Ths reversed string is $it") // "abc" 프린트
  it.length
}.also {  
  println("The length is $it")  // "abc" 프린트
}

original.also {
  prinln("The original string is $it")
}.also {
  println("Ths reversed string is ${it.reversed()}") // 리버스된 문자열 프린트
}.also {  
  println("The length is ${it.length}")  // 문자열 길이 프린트
}


위 샘플을 보면 also가 더이상 무슨 소용이 있나 싶을 수 있지만 잘생각해 보면 좋은 장점이 있다.

1. 동일 객체에 대한 일련의 처리를 명확하게 구분할 수 있게 해준다.
2. 체이닝 빌드 연산을 만듦으로 객체를 스스로 조작하는데 파워풀한 기능을 제공한다.

두가지를 합쳐서 한번은 변환하고 이어서 그 객체를 조작하는 체이닝을 만들 수 있다.

// 일반적인 접근
fun makeDir(path: String): File {
  val result = File(path)
  result.mkdirs()
  return result
}

// 향상된 접근
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }


모든 속성 살펴보기

T.apply로 3가지 속성을 살펴보는것으로 더 많은것을 알 수 있다.

1. 함수의 확장이 필요한가?
2. 인자로서 this를 전달이 필요한가?
3. this를 반환이 필요한가?

// 일반적인 접근
fun createInstance(args: Bundle) : MyFragment {
  val fragment = MyFragment()
  fragment.arguments = args
  return fragment
}

// 향상된 접근
fun createInstance(args: Bundle) = MyFragment().apply { argument = args }
fun createInstance(args: Bundle) = MyFragment().also { it.argument = args }



this를 반환하기 때문에 체이닝이 가능하다.

// 일반적인 접근
fun createIntent(intentData: String, intentAction: String) : MyFragment {
  val intent = Intent()
  intent.action = intentAction
  intent.data = Uri.parse(intentData)
  return intent
}

fun createIntent(intentData: String, intentAction: String) = Intent()
    .apply { action = intentAction }
    .apply { data = intentData }

fun createIntent(intentData: String, intentAction: String) = Intent()
    .also { it.action = intentAction }
    .also { it.data = intentData }

위에서 설명한 표준함수들을 선택하는데 있어 선택장애자를 위한 표는 다음과 같다.

 




Posted by 삼스