꿈틀꿈틀 개발일기

20231213 / Scope Functions / let, with, also, apply, run

by jeongminy
Scope Functions

- 자기 자신의 객체를 전달해서 효율적인 처리를 할 수 있어요
- 객체를 사용할때임시로 Scope를 만들어서 편리한 코드 작성을 도와줘요

코틀린 문서 페이지 : https://kotlinlang.org/docs/scope-functions.html

 

let

let 함수는 null이 아닌 값에 대해서만 코드 블록을 실행시키기 위해 사용되는 Scope Functions입니다.
중괄호 블록안에 it으로 자신의 객체를 전달하고 수행된 결과를 반환해요
let 함수는 코드의 가독성과 유지보수성을 향상시키는 데 도움이 되는 유용한 함수입니다.

 

더보기
val car: Car? = null

car?.start()

위 코드는 car 가 null 이기 때문에 실행되지 않는다.

car?.let { it.start() }

그런데, 이 코드는 car가

null이면 -> 아무일도 일어나지 않아!
null이 아니면 -> start() 메소드를 호출함 (왜냐?? let 함수를 이용했기 때문에 it이 car의 값을 참조하게 되서!!)

따라서 위 코드는 아래의 코드와 동일하다고 할 수 있어.

car?.let {
  if (it != null) {
    it.start()
  }
}

 

 


 

with

중괄호 블록안에 this로 자신의 객체를 전달하고 코드를 수행해요
this는 생략해서 사용할 수 있으므로 반드시 null이 아닐때만 사용하는게 좋아요

 

더보기
		var alphabets = "abcd"

    with(alphabets) {
//      var result = this.subSequence(0,2) // this가 생략되어 있음을 설명
        var result = subSequence(0,2) // 아래처럼 쓰임
        println(result)
    }

 

 


 

also

중괄호 블록안에 it으로 자신의 객체를 전달하고 객체를 반환해줘요
apply와 함께 자주 사용해요

 

더보기
fun main() {
    var student = Student("참새", 10)

    var result = student?.also {
        it.age = 50 // also를 통해 it이 student객체를 받고, student객체를 반환함.
    }
    result?.displayInfo() // 출력 : 이름은 참새 입니다 나이는 50 입니다
    student.displayInfo() // 출력 : 이름은 참새 입니다 나이는 50 입니다
} // 두 출력이 동일하다는 것은 it이 student 객체를 반환함을 알려줌.

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

 


 apply

중괄호 블록안에 this로 자신의 객체를 전달하고 객체를 반환해줘요
주로 객체의 상태를 변화시키고 바로 저장하고 싶을때 사용해요

 

더보기
fun main() {
    var student = Student("참새", 10)

    var result = student?.apply { //apply가 사용됨
        student.age = 50 // this가 생략되어 있음
    }
    result?.displayInfo() // 출력 : 이름은 참새 입니다 나이는 50 입니다
    student.displayInfo() // 출력 : 이름은 참새 입니다 나이는 50 입니다
} // 위와 아래가 같음을 확인할 수 있다.

class Student(name: String, age: Int) { //Student클래스 정의
    var name: String
    var age: Int
    
    init { // 클래스의 생성자
        this.name = name
        this.age = age
    }
    
    fun displayInfo() { //클래스의 메소드
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 


run

run은 객체를 전달받아 처리하는 함수입니다.
run 함수는 객체를 it 으로 받아 사용할 수 있으며, 마지막으로 실행된 코드의 결과를 반환합니다.

 

1. 객체에서 호출하지 않는 경우의 예시

더보기
fun main() {
	var totalPrice = run { // run으로 중괄호안에 임시 영역을 만들고
        var computer = 10000 // 변수 두개를 정의하고
        var mouse = 5000

        computer+mouse // 두개를 더한것을 리턴해서 totalPrice에 넣어라
    }
    println("총 가격은 ${totalPrice}입니다")
}

 

 

2. 객체에서 호출하는 경우의 예시
- with와 달리 null체크를 수행할 수 있어서 더욱 안전 사용 가능

더보기
fun main() {
    var student = Student("참새", 10)
    student?.run {
        displayInfo()
    }
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int
    
    init {
        this.name = name
        this.age = age
    }
    
    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

 


 

정리

Kotlin에서 let, with, also, apply, run은 모두 scope function으로, 객체의 속성이나 함수를 처리하는 데 사용됩니다.

 

함수 설명
let
객체의 속성이나 함수를 처리하는 람다를 제공합니다. 람다의 반환값은 객체 자체입니다.
with
객체의 속성이나 함수를 처리하는 람다를 제공합니다. 람다의 반환값은 없습니다.
also
객체의 속성이나 함수를 처리하는 람다를 제공합니다. 람다의 반환값은 객체 자체입니다.
apply
객체의 속성이나 함수를 처리하는 람다를 제공합니다. 람다의 반환값은 객체 자체입니다.
run
객체의 속성이나 함수를 처리하는 람다를 제공합니다. 람다의 반환값은 람다 내부의 표현식의 값입니다.

 

  Scope에서 접근방식 this
Scope에서 접근방식 it
블록 수행 결과를 반환 run, with let
객체 자신을 반환 apply also

 

다음은 각 함수의 사용 예입니다.

더보기
fun main(args: Array<String>) {
    val person = Person("John Doe", 30)

    // `let` 사용
    val age = person.let { it.age }
    println(age) // 30

    // `with` 사용
    with(person) {
        println(age) // 30
    }

    // `also` 사용
    val result = person.also {
        println(it.age) // 30
    }
    println(result) // Person(name=John Doe, age=30)

    // `apply` 사용
    val result2 = person.apply {
        age = 31
    }
    println(result2) // Person(name=John Doe, age=31)

    // `run` 사용
    val result3 = person.run {
        age = 32
        println(age) // 32
    }
    println(result3) // 32
}

 


 

주의!!

모든 수신객체를 it으로 활용하면 문제가 발생할 수 있어요

 

더보기
// Scope Function을 중첩으로 사용할 경우 누가 누구의 범위인지 알수 없다!
// Implicit parameter 'it' of enclosing lambda is shadowed 경고 발생!

data class Person(
	var name: String = "",
	var age: Int? = null,
	var child: Person? = null
)

// 잘못된 예시
Person().also {
	it.name = "한석봉"
	it.age = 40
  val child = Person().also {
	  it.name = "홍길동" // 누구의 it인지 모른다!
    it.age = 10 // 누구의 it인지 모른다!
  }
  it.child = child
}

// 수정한 예시
Person().also {
	it.name = "한석봉"
	it.age = 40
  val child = Person().also { c -> // c를 이용하면 구분을 할 수 있다!!
	  c.name = "홍길동"
    c.age = 10
  }
  it.child = child
}

 

 

블로그의 정보

꿈틀꿈틀 개발일기

jeongminy

활동하기