KotlinInAction/람다로 프로그래밍

수신 객체 지정 람다 (with와 apply)

webmaster 2022. 8. 3. 01:36
728x90

with 함수

수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메서드를 호출할 수 있게 하는 람다를 수신 객체 지정 람다라 부르는데, with는 수신 객체 지정 람다를 활용한다.

어떤 객체의 이름을 반복하지 않고도 그 객체에 대해 다양한 연산을 수행할 수 있다(코틀린에서는 with라는 라이브러리 함수를 제공한다)

fun main(args: Array<String>) {
    println(alphabet())
}

fun alphabet(): String{
    val result = StringBuilder()
    for (letter in 'A'..'Z'){
        result.append(letter)
    }
    result.append("\nNow I know the alphabet!")
    return result.toString()
}
  • result에 대해 다른 여러 메서드를 호출하면서 매번 result를 반복 사용했다.
fun main(args: Array<String>) {
    println(alphabet())
}

fun alphabet(): String{
    val stringBuilder = StringBuilder()
    return with(stringBuilder){//메서드를 호출하려는 수신객체를 지정
        for (letter in 'A'..'Z'){
            this.append(letter)//this 를 명시해서 앞에서 지정한 수신객체의 메서드를 호출
        }
        append("\nNow I know the alphabet!") //this 생략 가능
        this.toString()//람다에서 값 반호나
    }
}
  • with의 첫 번째 파라미터는 stringBuilder이고, 두 번째 파라미터는 람다이다.
    • 람다를 괄호 밖으로 빼내는 관례를 사용함에 따라 전체 함수 호출이 언어가 제공하는 특별한 구문처럼 보인다.
  • with함수는 첫 번째 인자로 받은 객체를 두 번째 인자로 받은 람다의 수신 객체로 만든다.
    • 인자로 받은 람다 본문에서는 this를 사용해 수신 객체에 접근할 수 있다.
    • this와. 을 사용하지 않고 프로퍼티나 메서드 이름만 사용해도 수신 객체의 멤버에 접근할 수 있다.
fun main(args: Array<String>) {
    println(alphabet())
}

fun alphabet(): String{
    return with(StringBuilder()){
        for (letter in 'A'..'Z'){
            append(letter)
        }
        append("\nNow I know the alphabet!") 
        toString()
    }
}
  • 불필요한 stringBuilder 변수를 없애 alphabet 함수가 식의 결과를 바로 반환하게 한다.
    • 식을 본문으로 하는 함수로 표현할 수 있다
  • StringBuilder의 인스턴스를 만들고 즉시 with에게 인자로 넘겨 람다 안에서 this를 사용해 그 인스턴스를 참조한다.

TIP : 메서드 이름 충돌

만약 with에게 넘긴 객체의 클래스와 with에서 사용하는 코드가 들어있는 클래스 안에 이름이 같은 메서드가 있다면 어떻게 될까? 

this 참조 앞에 레이블을 붙이면 호출하고 싶은 메서드를 명확하게 정할 수 있다.

apply 함수

with와 흡사하지만, 차이는 항상 자신에게 전달된 객체를 반환한다는 것이다.

fun main(args: Array<String>) {
    println(alphabet())
}

fun alphabet() = StringBuilder().apply {
    for (letter in 'A'..'Z') {
        append(letter)
    }
    append("\nNow I know the alphabet!")
}.toString()
  • apply는 확장함수로 정의되어 있다.
    • apply의 수신 객체 가수 신 객체가 전달받은 람다의 수신 객체가 된다.
  • 객체의 인스턴스를 만들면서 즉시 프로퍼티중 일부를 초기화해야 되는 경우 사용하기 유용하다.
    • 자바에서는 보통 Builder 객체가 이런 역할을 담당하며, 코틀린에서는 라이브러리의 특별한 지원 없이도 클래스의 인스턴스에 대해 apply를 활용할 수 있다
fun createViewWithCustomAttributes(context: Context){
    TextView(context).apply{
        text = "Sample Text"
        textSize = 20.0
        setPadding(10, 0, 0, 0)
    }
}
  • apply 함수를 사용하면 함수의 본문에 간결한 식을 사용할 수 있다.
  • 새로운 TextView인스턴스를 만들고 즉시 인스턴스를 apply에 넘긴다.
    • apply에 전달된 람다 안에서는 TextView가 수신 객체가 된다.
    • 원하는 대로 TextView의 메서드를 호출하거나 프로퍼티를 설정할 수 있다.
  • 람다를 실행하고 나면 apply는 람다에 의해 초기화된 TextView인스턴스를 반환한다.
fun alphabet() = buildString {
    for (letter in 'A'..'Z') {
        append(letter)
    }
    append("\nNow I know the alphabet!")
}
  • 표준 라이브러리의 buildString을 사용하면 더 단순화할 수 있다.
  • buildString은 StringBuilder객체를 만드는 일과 toString을 호출해 주는 일을 알아서 해준다.
  • buildString의 인자는 수신 객체 지정 람다며, 수신 객체는 항상 StringBuilder가 된다.
728x90

'KotlinInAction > 람다로 프로그래밍' 카테고리의 다른 글

자바 함수형 인터페이스 활용  (0) 2022.08.03
지연 계산 컬렉션 연산  (0) 2022.08.02
컬렉션 함수형 API  (0) 2022.08.01
람다 식과 멤버 참조  (0) 2022.07.31