Lined Notebook

Scope Function

by HeshAlgo

Scope Function

Scope Function은 객체 컨텍스트 내에서 코드 블록을 실행할 수 있게 하는 함수입니다.

Lambda Expression이 있는 개체에서 Scope Function을 호출하면 임시 범위가 형성됩니다. 이 범위 내에서는 이름 없이 개체에 access할 수 있습니다. 이 Scope Function에는 let, run, with, apply, also 이렇게 5가지가 존재하는데 하나씩 살펴보도록 하겠습니다.

 

Function selection

Scope Function의 lambda expression 내부의 컨텍스트 객체는 실제 이름 대신 짧은 참조로 사용할 수 있습니다.

각 Scope Function이 컨텍스트 객체에 access 하는 두 가지 방법 중 하나를 사용합니다. 

lambda receiver(this), lambda argument(it

이렇게 두 가지를 사용합니다. 

let, also run, with, apply
it this

this

run, with 및 also는 컨텍스트 객체를 this를 통해 사용합니다. 대부분의 경우 this 수신자 객체의 멤버에 액세스할 때 생략이 가능해서 코드를 더 짧게 사용할 수 있습니다. 하지만 this를 생략하면 수신자 멤버와 외부 객체나 함수를 구분하기 어려울수도 있기 때문에 this를 사용하는 것을 권장합니다. 따라서 this 객체 멤버에 대해 주로 작동하는 람다의 경우 컨텍스트 객체를 receiver로 사용하는 것이 좋습니다. 

val adam = Person("Adm").apply {
    age = 20			 // same as this.age = 20 or adm.age = 20
    city = "London"
}

println(adm)			// Person(name=Adm, age=20, city=London)

 

it

let, also 컨텍스트 객체를 lambda argument 처럼 가지고 있습니다. 따라서 it을 통해 컨텍스트 객체에 접근할 수 있습니다. 객체의 함수나 프로퍼티를 호출할 때 this처럼 암시적으로 사용할 수는 없습니다. 따라서 it 객체가 함수 호출에서 인수로 주로 사용되는 경우 컨텍스트 객체를 그대로 사용하는 것이 좋습니다. 

fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

println(getRandomInt())

추가로 컨텍스트 객체를 argument로 전달한다면 컨텍스트 객체에 이름을 부여할 수 있습니다. 

fun getRandomInt(): Int {
    return Random.nextInt(100).also { value ->
        wrtieToLog("getRandomInt() generated value $value")
    }
}

println(getRandomInt())

 

Return value

Scope Function은 코드 블럭이 반환하는 것에서 차이가 있습니다. 

  • apply, also -> retun the context object.
  • let, run, with -> return the lambda result.

Context object

apply와 also는 해당 코드 블럭의 context object를 반환합니다. 따라서 이 두 함수는 같은 객체에 대한 call chain을 가질 수 있습니다.

val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }	// Populating the list
    .apply {
        this.add(2.71)
        this.add(3.14)
        this.add(1.0)
    }
    .also { println("Sorting the list") }	// Sorting the listSorting the list
    .sort()
println(numberList)					// [1.0, 2.71, 3.14]

 

또한 이들은 return 문에 사용될 수 있습니다.

fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

 

Lambda result

let, run, with는 lambda result를 반환합니다. 따라서 이 두 함수의 결과는 변수에 할당될 수 있습니다.

val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run { 
    add("four")
    add("five")
    count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")	// There are 3 elements that end with e.

추가적으로 return 결과값을 무시할수 있고 scope function을 변수에 대한 임시 범위를 생성하는데 사용할 수 있습니다.

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    val firstItem = first()
    val lastItem = last()        
    println("First item: $firstItem, last item: $lastItem") // First item: one, last item: three
}

 

Functions

let

let은 context object를 argumnet(it)처럼 사용할 수 있고 lambda result를 반환합니다.

let은 호출 체인의 결과에 대해 하나 이상의 함수를 호출하는데 사용할 수 있습니다.

val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)	// [5, 4, 4]

let을 사용하면 다음과 같이 사용할 수 있습니다.

val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let { 
    println(it)
    // and more function calls if needed
}

만약 코드블럭이 it을 인자로 가지는 함수 하나만 담고 있다면 lambda expression 대신 ::을 사용할 수 있습니다.

val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)

let은 종종 non-null 객체에 코드블럭을 생성할 때 사용합니다.

 

with

with는 context object를 argument로 전달하지만 내부는 this로 사용가능합니다. lambda result를 반환합니다.

with는 코드블럭 내에서 lambda result 반환없이 함수를 호출할 때 사용하는것을 추천합니다. 

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    println("'with' is called with argument $this")
    println("It contains $size elements")
}

다른 사용 사례는 속성이나 함수를 계산하는데 사용될수 있습니다.

val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
    "The first element is ${first()}," +
    " the last element is ${last()}"
}
println(firstAndLast)	// The first element is one, the last element is three

 

run

run은 context object를 this를 통해 이용가능하고 lambda result 결과값을 반환합니다.

run은 코드 블럭 내에서 객체 초기화와 반환 결과값 계산을 모두 포함하고 있을때 유용합니다.

val service = MultiportService("https://example.kotlinlang.org", 80)

val result = service.run {
    port = 8080
    query(prepareRequest() + " to port $port")
}

// the same code written with let() function:
val letResult = service.let {
    it.port = 8080
    it.query(it.prepareRequest() + " to port ${it.port}")
}

println(result)		// Result for query 'Default request to port 8080'
println(letResult)	// Result for query 'Default request to port 8080'

 

apply

apply는 context object를 this를 통해 이용가능하고 context object를 반환합니다.

apply는 코드블럭에 의해서 결과값을 반환하지 않고 주로 context object 멤버들로 동작하는 경우 사용합니다.

apply의 일반적인 케이스로는 객체를 구성할때입니다. 

val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}
println(adam)	// Person(name=Adam, age=32, city=London)

 

also

also는 context object를 argument(it)로 이용가능하고 context object를 반환합니다. 

also는 context object를 인자처럼 사용하는 경우에 유용합니다. 또한, 객체의 프로퍼티나 함수에 대한 참조가 아니라 객체 자체에 대한 참조가 필요한 액션에 유용합니다.

val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("The list elements before adding new one: $it") }
    .add("four")
// The list elements before adding new one: [one, two, three]

 

 

 

출처
https://kotlinlang.org/docs/scope-functions.html

'Language > 코틀린(Kotlin)' 카테고리의 다른 글

Iterator와 Sequence  (1) 2023.11.19
[Kotlin] 프로그램 흐름 제어  (0) 2021.03.31
[Kotlin] 코틀린 null 처리 및 자료형 변환  (0) 2021.03.30

블로그의 정보

꾸준히 공부하는 개발 노트

HeshAlgo

활동하기