코틀린에는 객체의 컨텍스트에서 코드를 실행하기 위한 몇 가지 범위 지정 함수가 있다.
let
`let` 함수는 주로 null이 아닌 객체에 대한 코드를 실행하거나, null 가능성이 있는 변수와 작업을 할 때 사용한다. 람다는 'it' 키워드를 통해 호출된 객체에 접근하며, 람다 내부의 마지막 표현식을 반환한다.
fun main() {
val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
item?.let { println(it) } // prints A and ignores null
}
}
// 실행 결과
A
run
`run` 함수는 람다 내에서 수신 객체('this')에 대한 작업을 수행한 후, 람다 내부의 마지막 표현식을 반환한다. 객체에 대한 여러 작업을 연속적으로 수행하려는 경우에 유용하다.
fun main() {
val result = "Hello".run {
println(this) // prints "Hello"
length // returns length of String "Hello"
}
println(result)
}
// 실행 결과
Hello
5
더 이해를 돕기 위한 예시
class Person {
var name = ""
var age = 0
var city = ""
override fun toString(): String {
return "Person(name='$name', age=$age, city='$city')"
}
}
fun main() {
val person = Person().run {
name = "John"
age = 25
city = "New York"
this
}
println(person.toString())
}
// 실행 결과
Person(name='John', age=25, city='New York')
apply
`apply` 함수는 객체의 초기화와 함께 그 객체를 반환하려는 경우에 유용하다. 함수 내에서 수신 객체('this')에 접근하며 그 객체 자체를 반환한다.
data class Person(var name: String, var age: Int, var city: String)
fun main() {
val person = Person("Adam", 20, "London").apply {
age = 21
city = "Manchester"
}
println(person)
}
// 실행 결과
Person(name=Adam, age=21, city=Manchester)
[TMI] 내가 주로 쓰고 있는 방법
fun main() {
val idList = arrayOf("muzi", "frodo", "apeach", "neo")
val idMap = mutableMapOf<String, Int>().apply { idList.forEach { this[it] = 0 } }
println(idMap)
}
// 실행 결과
{muzi=0, frodo=0, apeach=0, neo=0}
이런 식으로 map을 만들면서 바로 초기화까지 하고 싶은 상황에서 유용하게 한 줄로 사용하고 있다😁
also
`also` 함수는 주로 객체의 상태를 변경하거나, 객체를 로그에 기록하거나, 디버깅 코드를 추가하는 등의 부수적인 작업을 수행하는 데 사용된다. 함수 내에서 호출된 객체는 'it'로 참조되며, 그 객체 자체를 반환한다.
fun main() {
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
println(numbers)
}
// 실행 결과
The list elements before adding new one: [one, two, three]
[one, two, three, four]
with
`with` 함수는 인자로 전달된 객체에 대해 연산을 수행하고, 마지막 표현식을 반환한다. 수신 객체 'this'로 참조한다. `with`는 null이 아닌 객체에 대해 작업을 수행하는 경우에 유용하다.
data class Person(var name: String, var age: Int, var city: String)
fun main() {
val person = Person("Adam", 20, "London")
with(person) {
age = 21
city = "Manchester"
}
println(person)
}
// 실행 결과
Person(name=Adam, age=21, city=Manchester)
`apply`와 `with`은 비슷하네?
`apply`와 `with`는 모두 객체에 대해 여러 작업을 수행하는 데 유용하지만, 두 함수의 처리하는 방식과 반환하는 값에는 중요한 차이점이 있다.
- `apply`는 람다를 수신 객체의 컨텍스트에서 실행하고, 해당 객체 자체를 반환한다. 이는 주로 객체를 초기화하고 그 객체를 반환하는 데 사용된다.
- `with`는 첫 번째 인자로 받은 객체를 수신 객체로 사용하고, 람다의 마지막 표현식의 결과를 반환한다. 이는 주로 null이 아닌 객체에 대해 일련의 연산을 수행하고 그 결과를 반환하는 데 사용된다.
따라서 `apply`는 객체 자체를 반환해야 하고, `with`는 계산된 결과를 반환하려는 경우에 적합하다.
혹시나 람다를 정확히 모른다면?
람다(lambda)는 프로그래밍에서 매우 중요한 개념으로, '익명 함수'또는 '함수 리터럴'이라고도 불린다. 람다는 이름이 없는 함수를 정의하고 사용할 수 있게 해주는 문법이다.
람다는 일반적으로 다른 함수의 인자로 전달되거나, 변수에 할당되거나, 데이터 구조에 저장되는 등 다양한 방법으로 사용될 수 있다. 이런 특성 때문에 람다는 고차 함수(다른 함수를 인자로 받거나 반환하는 함수)를 작성하는 데 유용하다.
코틀린에서 람다는 중괄호 `{}`로 감싸인 코드 블록으로 표현된다. 람다의 매개변수는 `->` 앞에 정의하며, 함수 본문은 `->` 뒤에 작성한다.
간단한 예:
val add = { a: Int, b: Int -> a + b }
println(add(1, 2)) // 출력: 3
여기서 `add`는 두 개의 `Int` 매개변수를 받아 그 합을 반환하는 람다다.
위에서 다룬 범위 지정 함수들은 모두 람다를 인자로 받는 고차 함수다. 이 함수들을 사용하면 코드를 더 간결하고 유연하게 작성할 수 있다.
언제나 잘못된 설명이나 부족한 부분에 대한 피드백은 환영입니다🤍