본문 바로가기
Kotlin Language/Kotlin 기본 문법

Kotlin(코틀린) - 스코프 함수(let, with, run, apply, also) 정리

by Classic Master 2023. 12. 6.
728x90

스코프 함수 (Scope Function) ?

  • 코틀린에서는 스코프 함수라는 특별한 종류의 함수가 있습니다. 이 함수들은 객체의 범위 내에서 코드 블록을 실행하며, 주로 객체 초기화, 속성 설정, 연산 후 결과 반환 등의 작업에 사용됩니다.
  • 객체의 이름을 통해 하나하나 참조하지 않고 객체를 접근하고 핸들링 할 수 있는 장점이 있습니다.
  • 주요한 코틀린 스코프 함수에는 let, run, with, apply, also 다섯 가지가 있습니다.

let

  • let 함수는 수신 객체를 람다 함수의 인자로 전달하고, 람다 함수의 결과를 반환합니다.
    주로 null 체크 후에 안전하게 수행해야 할 작업이 있을 때 사용됩니다.
data class Person(var name: String, var age: Int)

val person = Person("John", 25).let { p ->
    // 객체 초기화와 속성 설정
    p.age += 5
    p.name = "John Doe"
    // let 블록의 마지막 표현식은 반환값으로 사용됨
    p
}
  • 객체 초기화 및 속성 설정시 사용할 수 있습니다.
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
    println(it)
    // 추가적인 함수 호출
    val sum = it.sum()
    println("Sum of lengths: $sum")
}
  • 추가로 함수 호출도 가능합니다.

 

with

  • 주로 수신 객체의 메소드나 속성에 직접 접근하는데 사용됩니다. with를 사용하면 코드 블록 내에서 수신 객체의 멤버에 더 편리하게 접근할 수 있습니다.
data class Person(var name: String, var age: Int)

fun main() {
    val person = Person("John", 30)
    val result = with(person) {
        // 이 블록에서는 person의 멤버에 직접 접근할 수 있음
        println("Person's name: $name, age: $age")
        // 코드 블록의 마지막 표현식은 반환값으로 사용됨
        "$name is $age years old."
    }
    println(result) // 출력: "John is 30 years old."
}
  • with 함수를 사용하면 특정 객체의 멤버에 접근할 때 객체 이름을 반복해서 쓰지 않아도 되기 때문에 코드를 더 간결하게 작성할 수 있습니다.
val numbers = listOf(1, 2, 3, 4, 5)

val filteredNumbers = with(numbers) {
    // 리스트의 filter() 함수에 접근하여 필터링
    this.filter { it % 2 == 0 }
}
println(filteredNumbers)
  • with 함수 내에서 this를 사용하여 수신 객체의 멤버에 직접 접근하고 있습니다. with를 사용하면 여러 메소드나 속성에 접근할 때 특히 코드를 더 간결하게 유지할 수 있습니다.

 

run

  • 주로 객체의 초기화와 함께 코드 블록을 실행하고자 할 때 사용됩니다. run 함수는 수신 객체를 람다 함수의 수신자로 전달하고, 람다 함수의 결과는 코드 블록의 마지막 표현식이 됩니다.
data class Person(var name: String, var age: Int)

fun main() {
    val person = Person("John", 30)
    val result = person.run {
        // 이 블록에서는 person의 멤버에 직접 접근할 수 있음
        println("Person's name: $name, age: $age")
        // 코드 블록의 마지막 표현식은 반환값으로 사용됨
        "$name is $age years old."
    }
    println(result) // 출력: "John is 30 years old."
}
  • run 함수를 사용하면 특정 객체의 멤버에 접근할 때 객체 이름을 반복해서 쓰지 않아도 되기 때문에 코드를 더 간결하게 작성할 수 있습니다.
class Car(var brand: String, var model: String, var year: Int)
fun main() {
    val myCar = Car("Toyota", "Camry", 2022).run {
        // 객체 초기화와 속성 가공
        this.year += 1
        "My car is a $brand $model from the year $year."
    }
    println(myCar)
}
  • this 키워드를 사용하여 현재 수신 객체에 접근하고, year 속성을 증가시킵니다.

 

apply

  • 수신 객체를 초기화하고 수정하는데 사용됩니다. 주로 객체를 생성하면서 여러 속성을 초기화할 때 유용하게 활용됩니다.
  • apply 함수의 특징은 람다 블록 내에서 수신 객체의 멤버에 직접 접근할 수 있다는 것입니다. 블록의 마지막 표현식은 수신 객체 자체이며, 반환값은 수신 객체입니다.
class Car(var brand: String, var model: String, var year: Int)
fun main() {
    val myCar = Car("Toyota", "Camry", 2022).apply {
        // 객체 초기화 및 속성 가공
        year += 1
        // 다른 속성 초기화 가능
    }
    println("My car is a ${myCar.brand} ${myCar.model} from the year ${myCar.year}.")
}
  • apply 함수를 사용하여 Car 클래스의 인스턴스를 초기화하고, 블록 내에서 year 속성을 증가시켰습니다. 이렇게 하면 객체를 생성하면서 속성을 초기화할 때 코드를 더 간결하게 유지할 수 있습니다.
class Movie(var title: String, var genre: String, var releaseYear: Int)
val movie = Movie("", "", 0).apply {
    // 객체의 속성 초기화 및 가공
    this.title = "Inception"
    this.genre = "Sci-Fi"
    this.releaseYear = 2010
}
println("Movie details: ${movie.title}, ${movie.genre}, released in ${movie.releaseYear}.")
  • apply 함수 내에서 this를 사용하여 수신된 객체의 멤버에 직접 접근하고 있습니다. this를 사용하면 객체의 속성에 더 명시적으로 접근할 수 있으며 코드를 더 명확하게 만들 수 있습니다.

 

also

  • 수신 객체를 그대로 반환하면서, 람다 블록 내에서 수신 객체를 사용하여 원하는 작업을 수행합니다.
  • also 함수의 특징은 람다 블록 내에서 수신 객체를 it이라는 이름으로 사용할 수 있다는 것입니다. 블록 내에서의 작업이 주로 로깅, 추가적인 처리, 또는 디버깅과 같은 목적으로 활용됩니다.
class Person(var name: String, var age: Int)
fun main() {
    val person = Person("Alice", 25).also {
        // also 블록 내에서 수신 객체에 작업 수행
        it.age += 5
        println("Person details inside 'also': ${it.name}, ${it.age} years old.")
    }
    println("Person details outside 'also': ${person.name}, ${person.age} years old.")
}
  • also 함수를 사용하여 Person 클래스의 인스턴스를 초기화하고, 블록 내에서 age 속성을 수정하고 출력합니다. 이후에도 also 블록 이후의 코드에서 person 객체를 사용할 수 있습니다.
class Book(var title: String, var author: String, var pageCount: Int)
val myBook = Book("", "", 0).also {
    // 블록 내에서 it을 사용하여 객체의 속성 초기화
    it.title = "The Catcher in the Rye"
    it.author = "J.D. Salinger"
    it.pageCount = 224
}
println("Book details: ${myBook.title} by ${myBook.author}, ${myBook.pageCount} pages.")
  • also 함수를 사용하여 객체를 초기화하고 속성을 설정하며, 블록 내에서 it을 활용하여 수신 객체에 직접 접근하고 있습니다. 이렇게 함으로써 코드를 더 간결하게 작성하고 가독성을 높일 수 있습니다.

This?  It?

This

  • this는 클래스나 확장 함수 내에서 현재 객체에 대한 참조를 나타냅니다.
  • 클래스의 멤버 함수나 클래스 내부에서 this를 사용하면 그것은 현재 객체를 가리킵니다.

It

  • it은 람다 함수 블록 내에서 수신된 객체를 참조하는 데 사용됩니다. 
  • it은 이름이 고정되어 있으며 블록 내에서 수신된 객체에 직접 접근할 때 사용됩니다.
함수 키워드
let it
with this
run this
apply this
also it

차이점

let

  • 사용 목적: 람다 블록을 수행하면서 결과 값을 반환하고자 할 때 사용합니다.
    사용 시점: Nullable 객체에 대한 작업을 수행하고 결과를 반환하거나, 체이닝된 호출을 할 때 사용합니다.

with

  • 사용 목적: 특정 객체에 대한 코드 블록을 실행하면서 그 객체의 멤버에 직접 접근하고자 할 때 사용합니다.
    사용 시점: 객체의 멤버에 접근하는 코드를 좀 더 간결하게 표현하고자 할 때 사용합니다.

run

  • 사용 목적: 특정 객체에 대한 코드 블록을 실행하면서 그 객체의 멤버에 접근하고자 할 때 사용합니다. let과 유사하지만, 확장 함수 형태로 사용되어 더 명시적인 코드를 작성할 때 주로 활용됩니다.
    사용 시점: 객체의 초기화 또는 연산을 수행하면서 결과 값을 반환하고자 할 때 사용합니다.

apply

  • 사용 목적: 특정 객체의 속성을 초기화하거나 수정하면서 그 객체 자체를 반환하고자 할 때 사용합니다.
    사용 시점: 객체의 속성을 초기화하거나 수정하는 작업을 수행하면서 객체 자체를 반환하고자 할 때 사용합니다.

also

  • 사용 목적: 특정 객체에 대한 작업을 수행하면서 그 객체 자체를 반환하고자 할 때 사용합니다. apply와 유사하지만, 블록 내에서 it 키워드를 사용하여 더 명시적으로 객체에 접근할 수 있습니다.
    사용 시점: 객체에 대한 부가적인 작업을 수행하면서 객체 자체를 반환하고자 할 때 사용합니다.
728x90
반응형