Kotlin에서 자주 사용하는 스코프 함수인 run, let, apply, also, with에 대해 정리합니다. 각 함수는 블록 내에서 객체의 컨텍스트를 다루고, 해당 블록의 결과나 객체를 반환하는 방식으로 코드를 간결하고 명확하게 만들어 줍니다. 이 스코프 함수들은 객체에 대한 작업을 효율적으로 처리할 때 자주 사용되며, 그 차이점은 주로 반환값과 객체 접근 방식(this, it)에 있습니다.

run – 블록의 마지막 표현식을 반환 (this 사용)

  • 참조 방식: this (생략 가능)
  • 반환값: 블록의 마지막 표현식
  • 활용 예: 객체에 대해 여러 연산을 수행하고 하나의 값을 반환할 때.

run 함수는 주어진 블록을 실행하고 마지막 표현식을 반환합니다.
this 키워드를 통해 객체에 접근하며, 연산된 결과를 리턴하는 데 유용합니다.

val person = Person("Zenn", 25, "남성")

val details = person.run {
    val nameLength = name.length
    val isAdult = age >= 18
    val upperCaseGender = gender.uppercase()
    "$name의 이름 길이는 $nameLength, 성인 여부: $isAdult, 성별: $upperCaseGender."
}
println(details)
// "Zenn의 이름 길이는 4, 성인 여부: true, 성별: 남성."

위 코드에서 run을 통해 person 객체의 속성을 활용하여 연산을 수행하고 마지막 줄에서 그 결과를 반환합니다.

let – 블록의 마지막 표현식을 반환 (it 사용)

  • 참조 방식: it
  • 반환값: 블록의 마지막 표현식
  • 활용 예: 객체의 속성을 가공하거나 임시 변수를 만들 때.

let 함수는 주로 객체를 it으로 참조하며, 마지막 표현식을 반환합니다.
주로 객체가 null이 아닌 경우에 안전하게 작업을 수행하고, 그 결과를 반환할 때 많이 사용됩니다.

val person = Person("Zenn", 25, "남성")

val ageInMonths = person.let {
    val years = it.age
    val months = years * 12
    months
}
println(ageInMonths)  
// 300 (25세를 개월 수로 변환)

여기서 letit으로 객체를 참조하며, 마지막에 months 값을 반환합니다. 주로 데이터를 변환하거나 가공할 때 유용합니다.

apply – 객체 자체를 반환 (this 사용)

  • 참조 방식: this (생략 가능)
  • 반환값: 객체 자신
  • 활용 예: 객체의 속성을 설정하고, 수정된 객체를 반환하고 싶을 때.

apply 함수는 객체의 속성을 변경하거나 초기화할 때 자주 사용됩니다.
블록 내에서는 this로 객체에 접근하며, 블록을 실행한 후 객체 자체를 반환합니다.

val person = Person("Zenn", 25, "남성")

val updatedPerson = person.apply {
    name = "홍길동"
    age += 5
    gender = "기타"
}
println(updatedPerson)  
// Person(name=홍길동, age=30, gender=기타)

apply는 객체의 속성을 변경하고, 마지막에 수정된 객체 자체를 반환합니다.
주로 객체를 초기화하거나 설정할 때 많이 사용됩니다.

also – 객체 자체를 반환 (it 사용)

  • 참조 방식: it
  • 반환값: 객체 자신
  • 활용 예: 객체를 반환하면서 로그나 추가 작업을 할 때.

also 함수는 it을 사용해 객체에 접근하고, 블록 내에서 부수적인 작업(예: 로그 출력)을 한 후, 객체 자체를 반환합니다.
객체를 그대로 사용하면서 로그를 찍거나 추가 작업을 할 때 유용합니다.

val person = Person("Zenn", 25, "남성")

val updatedPerson = person.also {
    println("수정 전: ${it.name}, ${it.age}, ${it.gender}")
    it.age += 1  // 나이를 1 증가
    println("수정 후: ${it.name}, ${it.age}, ${it.gender}")
}
println(updatedPerson)
// 수정 전: Zenn, 25, 남성
// 수정 후: Zenn, 26, 남성
// Person(name=Zenn, age=26, gender=남성)

also는 부수적인 작업을 처리하고, 객체를 반환할 때 유용합니다.
객체의 상태를 로깅하거나 디버깅할 때도 많이 사용됩니다.

with – 동일 객체에 여러 작업을 수행하고 마지막 표현식을 반환

  • 참조 방식: this (생략 가능)
  • 반환값: 블록의 마지막 표현식
  • 활용 예: 여러 속성을 한꺼번에 처리할 때 유용.

with 함수는 주어진 객체에 대해 여러 작업을 수행하고, 마지막 표현식을 반환합니다.
주로 객체를 반복적으로 참조할 때 사용되며, 코드의 중복을 줄이는 데 유용합니다. 뷰 바인딩 같은 경우 자주 사용됩니다.

val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

// 뷰 바인딩 작업에서 여러 뷰에 대해 반복적인 작업을 할 때
with(binding) {
    button.text = "버튼을 눌러주세요!"
    textView.text = "환영합니다!"
    imageView.setBackgroundColor(Color.BLUE) // 이미지 대신 배경색을 설정
}

with를 사용하면 binding 객체를 반복적으로 참조하지 않고, 객체 내부의 속성에 간단하게 접근할 수 있어 코드가 간결해집니다.

정리

runlet: 블록의 마지막 표현식을 반환하며, 객체를 가공하거나 계산된 결과를 반환할 때 사용됩니다.

  • runthis로 객체에 접근
  • letit으로 객체에 접근

applyalso: 객체 자체를 반환하며, 속성 초기화나 부수적 작업을 수행할 때 사용됩니다.

  • applythis로 객체에 접근하여 속성 변경
  • alsoit으로 객체에 접근하며 부수적인 작업 처리

with: 주어진 객체에 대해 여러 작업을 수행하고, 마지막 표현식을 반환합니다. 주로 동일 객체에 대해 여러 속성을 다룰 때 유용하며, 코드의 중복을 줄여줍니다.