본문으로 바로가기

0. 소개

코틀린에서는 스코프 함수(scope functions)를 제공하는데,

이는 코드를 더욱 간결하고 가독성 높게 작성할 수 있게 돕습니다.

각각의 함수는 객체의 컨텍스트에서 블록 내의 코드를 실행하는 역할을 합니다.

 

1. let

let 함수는 객체를 이 함수의 블록 내부로 전달하며, 블록의 결과값을 반환합니다.

이 함수는 특정 객체가 null이 아닌 경우에만 코드를 실행하려는 경우, 즉 null safe programming에서 유용하게 사용됩니다.

→ '?.let'

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

→ 그냥 let만 쓰면 그냥 it으로 접근할 수 있다, 깔끔하게 코드를 작성할 수 있다 정도이고 ’?.let’ 이런 식으로 자주 쓰입니다.

‘?.’ : null 일 경우 null 반환 (즉, null 일 경우 해당 객체나 함수를 더 이상 참조하지 않고 null 반환)

’?.let’ : null 일 경우 let 함수는 실행되지 않음

var a: String? = null
var b: String = "hi"

a?.length // a가 null 이라면 String 객체의 length를 참조하지 않고 null을 반환
b.length

// 그에 따라 a가 null 일 경우 let 함수는 실행되지 않는다.
a?.let {
	println(a.length)
}

 

2. with

with 함수는 객체를 이 함수의 블록 내부로 전달하고, 블록의 결과값을 반환합니다.

이 함수는 this를 사용하여 객체를 참조합니다.

그러나, run 함수와 달리 with 함수는 람다 외부에서 객체를 받아서 넘겨줍니다.

따라서 주로 객체의 메소드를 연속적으로 호출할 때 사용됩니다.

val myStringList: MutableList<String> = mutableListOf("First", "Second", "Third")
with(myStringList) {
    add("Fourth")
    removeAt(0)
    reverse()
}

 

3. run

run 함수는 letwith의 조합과 같습니다.

run객체를 이 함수의 블록 내부로 전달하고, 블록의 결과값을 반환합니다.

그러나 이 함수는 this를 사용하여 객체를 참조합니다.

따라서, 객체의 초기화와 결과 계산을 한 번에 수행하고자 할 때 유용합니다.

// 1. 객체 초기화:
data class Person(var name: String, var age: Int)

val person = Person("John", 30).run {
    name = "Peter"
    age += 5
    this // person 객체 반환
}

println(person) // 출력: Person(name=Peter, age=35)
// 2. 연속된 메서드 호출:
class Calculator {
    var result = 0

    fun add(num: Int) {
        result += num
    }

    fun multiply(num: Int) {
        result *= num
    }
}

val calculator = Calculator().run {
    add(5)
    multiply(3)
    this // calculator 객체 반환
}

println(calculator.result) // 출력: 15
// 3. 범위 내 연산:
val result = run {
    val a = 10
    val b = 20
    a + b // 결과 반환
}

println(result) // 출력: 30

 

4. apply

apply 함수는 객체를 이 함수의 블록 내부로 전달하고, 객체 자체를 반환합니다.

이 함수는 this를 사용하여 객체를 참조합니다.

따라서, 객체를 초기화하고 그 객체를 바로 반환하고자 할 때 유용합니다.

data class Person(var name: String, var age: Int, var city: String)
val person = Person("Adam", 20, "London").apply {
    age = 21
    city = "Manchester"
}

 

5. also

also 함수는 let과 동일하게 동작하지만, this 대신에 it를 사용하여 객체를 참조합니다.

따라서, 객체의 상태를 변경하지 않고 객체에 대한 추가 작업을 수행하고자 할 때 사용됩니다.

also는 블록 결과 대신 객체 자체를 반환합니다.

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

 

6. it vs this

this와 it는 코틀린의 람다 표현식에서 사용되는 키워드

이 둘 사이의 차이점은 그것들이 참조하는 객체에 있습니다.

this

[this] 

: this는 람다를 호출하는 객체를 참조합니다.
: apply, run, with와 같은 함수에서 사용됩니다.
: 이런 경우 this는 명시적으로 사용되지 않아도 함수 또는 프로퍼티를 직접 호출하는 것이 가능합니다.

data class Person(var name: String, var age: Int)
val person = Person("John", 30).apply {
    name = "James" // 'this' is implicit
}

it

[it]

: it는 람다 표현식에서 단일 매개변수를 참조합니다.

: let, also와 같은 함수에서 사용됩니다.

: it를 사용하면 별도의 명시적 매개변수 선언이 필요 없습니다.

data class Person(var name: String, var age: Int)
val person = Person("John", 30).let {
    it.name = "James" // 'it' refers to the person object
}

 

정리

1. let   : 특정 객체가 null이 아닌 경우에만 코드를 실행하려는 경우 주로 사용 ('?.let') = it 참조
3. with  : run 함수와 달리 with 함수는 람다 외부에서 객체를 받아서 넘겨줌, 객체의 메소드를 연속적으로 호출할 때 사용 = this 참조
2. run   : let과 with의 조합, 객체의 초기화와 결과 계산을 한 번에 수행하고자 할 때 유용 = this 참조
4. apply : 객체를 초기화하고 그 객체를 바로 반환하고자 할 때 유용 = this 참조
5. also  : 객체의 상태를 변경하지 않고 객체에 대한 추가 작업을 수행하고자 할 때 사용, 블록 결과 대신 객체 자체를 반환 = it 참조