본문 바로가기
@silver-w2025. 10. 10. 16:54
  1. scope function이란?
  2. scope function의 종류
  3. 언제 어떤 scope function을 사용해야 하나?
  4. scope function과 가독성

더보기

1 . scope function이란? 

 - 람다를 사용해 일시적인 영역을 만들고, 코드를 더 간결하게 만들거나 method chaning에 활용하는 함수

 

2 . scope function의 종류

종류

let 확장함수 람다의 결과를 반환
run
alse 람다의 결과와 무관하게
객체 그 자체를 반환
apply
with 확장함수가 아님  
fun printPerson(person: Person?) {
    val value1 = person.let {	// 람다의 결과값인 age가 value1 에 들어간다
        it?.age
    }

    val value2 = person.run {	// 람다의 결과값인 age가 value1 에 들어간다
        this?.age
    }

    val value3 = person.also {	// 객체인 person이 들어간다
        it?.age
    }

    val value4 = person.apply {	// 객체인 person이 들어간다
        this?.age
    }

    with(person) {
        print(name)
        print(this.age)
    }
}

 - 확장함수에 집어넣은 람다에서 수신객체를 호출할 때, it을 쓰나 this를 쓰냐 차이가 있다. 

this 생략 가능, 다른 이름을 붙일 수 없다.
it 생략 불가, 다른 이름을 붙일 수 있다.
fun printPerson(person: Person) {
    val value1 = person.let { p ->	// it
        p.age
    }

    val value2 = person.run {		// this
        age
    }
}​

 


let, also / run, apply 코드 
  - block 파라미터는 [T:함수 -> R:반환] 즉, 함수를 실행하여 결과값을 사용한다.

// let의 block(매개값)은 람다를 받아 그 결과값을 반환한다
public inline fun <T, R> T.let(block: (T) -> R): R {return block()}

public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}


// run, apply의 block(매개값)은 확장함수를 받아 그 결과값을 반환한다
public inline fun <T, R> T.run(block: T.() -> R): R {return block()}

public inline fun <T> T.apply(block: T.() -> Unit): T {
	block()
    return this
}

 


□ with 코드
 - with(파리미터, 람다) : this를 사용해 접근
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}



3 . 언제 어떤 scope function을 사용해야 하나?

□ let
 ① 하나 이상의 함수를 call chain 결과로 호출할 때 사용
 ② non-null 값에 대해서만 code block을 실행 (자주 사용)
 ③ 일회성으로 제한된 영역에 지역 변수를 만들 때 (depth 이슈로 권장 x)

fun main() {
	// 1
    val String = listOf("apple", "ba", "orange")
    String.map { it.length }
        .filter {it > 3}
        .let(::println)	// .let { len -> println(len) } 과 같음
        // let은 앞의 결과 List<Int>에 대해서 호출
	
    // 2
	val str = "apple"
    val length = str?.let {
        println(it.uppercase())
        it.length
    }

	// 3
    val numbers = listOf("one", "two", "three", "four")
    val modifiedFirstItem = numbers.first()
        .let { firstItem ->
            if (firstItem.length >= 5) firstItem else "!${firstItem}"
        }.uppercase()
    print(modifiedFirstItem)

}


 run
 ① 객체 초기화와 반환 값의 계산을 동시에 해야 할 때
  - 1. 대체 와 같은 코드로 사용해도 되기 때문에 잘 사용하진 않는다. 반복되는 생성 후 처리(①의 hobby부분)는 생성자, 프로퍼티, init block으로 넣는 것이 좋다.

 - 생성자가 너무 길어지는 경우 사용하면 이를 이용해서 코드 가독성이 좋은 걸 선택

// 1. 객체를 만들어 DB에 바로 저장하고 그 인스턴스를 활용
val person1 = Person("홍길동", 20).run(personRepository::save)
val person2 = Person("홍길동", 20).run {
	hobby = "독서"
    personRepository.save(this)
}

// 1. 대체
val person = personRepository.save(Person("홍길동", 20)


  apply : 객체 그 자체가 반환 된다.
 ① 객체 설정을 할 때 객체를 수정하는 로직이 call chain 중간에 필요할 때 사용
  - 예로 들어 회원가입 할 때 이름/나이만 입력하고 나중에 정보 수정할 때 취미를 입력하는 경우 
  - 생성자에는 '취미' 프라퍼티가 들어가지 않는다. (text fixture)

fun createPerson(
    name: String,
    age: Int,
    hobby: String,
): Person {
   return Person(
       name = name,
       age = age,
   ). apply {
       this.hobby = hobby
   }
}


  also : 객체 그 자체가 반환 된다.
 ① 객체 "수정"하는 로직이 call chain 중간에 필요할 때

var balance: Int = 500

balance.also {println("Balance 입금 이전: $balance")}   // 500
    .plus(500)
    .let{ it -> println("입금 후 $it")} // 1000


  with : 특정 객체를 다른 객체로 변환해야 하는데, 모듈 간 의존성에 의해 정적 팩토리 혹은 toClass 함수를 만들기 어려울 때 사용 (this 생략으로 간결해짐)

fun createPerson(person: Person) {
    return with(person) {
        PersonDto(
            name = name,
            age = age,
        )
    }
}

 

4 . scope function과 가독성

- scope function을 활용한 것과 아닌 것을 비교하면

▶ 구현 1이 디버깅이 더 쉽고 수정이 더 쉽다, 자바 개발자 입장에서는 구현 1이 더 가독성이 좋다

- view:: showPerson()이 null을 반환한다면 let이하에서 예기치 않는 버그가 발

// 구현1
fun checkPs(person: Person, view: View) {
    if(person != null && person.age > 18) {
        view.showPerson(person)
    } else {
        view.showError()
    }
}

// 구현2
fun checkPs(person: Person, view: View) {
    person.takeif {it.age > 18}
        ?.let(view::showPerson)
        ?: view.showError()
}

 

▶ 적절한 convention을 적용하면 유용하게 활용할 수 있다. 
 

 


 

※ (intellij) 코틀린 코드 자바로 변환
tool > Kotlin > Show Kotlin Bytecode


출처 : https://inf.run/sMPv5

 

자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)| 최태현 - 인프런 강의

현재 평점 5.0점 수강생 3,556명인 강의를 만나보세요. 이 강의를 통해 Kotlin 언어의 특성과 배경, 문법과 동작 원리, 사용 용례, Java와 Kotlin을 함께 사용할 때에 주의할 점 등을 배울 수 있습니다. Ko

www.inflearn.com

 

silver-w
@silver-w :: silver-w 님의 블로그

silver-w 님의 블로그 입니다.

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차