티스토리 뷰

Kotlin 표준 라이브러리는 다양하고 편리한 기능으로 함수형 프로그래밍을 쉽게 적용할 수 있도록 도와준다.

그 중에서 apply, with, also, let, run가 있는데 이는 서로 비슷하면서 다르기 때문에 적절한 상황에 잘 사용해야 한다. 어떤 상황에서 어느 함수를 사용하면 좋은지 구분하기 위해 정리를 해 보았다.

 

 

먼저, 아래와 같이 Kotlin의 함수를 구분하는 유명한 표가 있다.

 

처음 위의 표를 봤을 때는 이해가 잘 되지 않았지만 아래 5가지 함수의 정의를 보고 의미를 이해할 수 있었다.

// with - 호출 시 수신 객체 T가 parameter로 명시적 전달
// 코드 블럭으로 수신 객체 T가 receiver로 암시적 전달 & 코드 수행 결과 반환
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

// also - 호출 시 수신 객체 T가 receiver로 암시적 전달
// 코드 블럭으로 수신 객체 T가 parameter로 명시적 전달 & 수신 객체 반환
inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

// apply - 호출 시 수신 객체 T가 receiver로 암시적 전달
// 코드 블럭으로 수신 객체 T가 receiver로 암시적 전달 & 수신 객체 반환
inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

// let - 호출 시 수신 객체 T가 receiver로 암시적 전달
// 코드 블럭으로 수신 객체 T가 parameter로 명시적 전달 & 코드 수행 결과 반환
inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

// run - 호출 시 수신 객체 T가 receiver로 암시적 전달
// 코드 블럭으로 수신 객체 T가 receiver로 암시적 전달 & 코드 수행 결과 반환
inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

 

 

사용 규칙

그렇다면 이제 어느 상황에 어떤 함수를 사용하는 것이 적합한지 사용 규칙을 알아보자.

 

[with]

  • 객체가 Non-nullable이고 결과가 필요하지 않은 경우
  • 같은 객체를 여러 번 호출할 때 호출부가 길어지고 복잡해지는 것을 해결할 수 있다. 

ex) 데이터베이스 Insert 시 ContentValues()에 컬럼에 대한 값 세팅

val memoValues = ContentValues()
with(memoValues) {
    put(MemoEntry._ID, memo.id)
    put(MemoEntry.TITLE_COL, memo.title)
    put(MemoEntry.CONTENT_COL, memo.content)
}

 

 

[also]

  • 객체의 속성을 변경하지 않고 사용하는 경우 (자기 자신 return)

ex) 객체의 side effect 확인, 객체 프로퍼티의 데이터 유효성 검사

class Book(author: Person) {
    val author = author.also {
        requireNotNull(it.age)
        print(it.name)
    }
}

 

 

[apply]

  • 내부 메서드를 사용하지 않고 프로퍼티만을 사용하는 경우 (자기 자신 return)

ex) 인스턴스 초기화

val peter = Person().apply {
    name = "Peter"
    age = 18
}

 

 

[let]

  • 지정된 값이 null이 아닌 경우에 코드를 실행
  • Nullable 객체를 다른 nullable 객체로 변환
  • 단일 지역 변수의 범위 제한

ex)

getNullablePerson()?.let {
    // null 이 아닐때만 실행
    promote(it)
}

val driversLicence: Licence? = getNullablePerson()?.let {
    // nullable personal객체를 nullable driversLicence 객체로 변경
    licenceService.getDriversLicence(it)
}

getPersonDao().let {
    // 변수 person 의 범위를 이 블록 안으로 제한
    val person: Person = getPerson()
    it.insert(person)
}

 

 

[run]

  • 어떤 값을 계산할 필요가 있거나 여러 개의 지역변수의 범위를 제한 (수행 결과 return)
  • 매개 변수로 전달된 명시적 수신객체 를 암시적 수신 객체로 변환

ex)

val inserted: Boolean = run {
    // person 과 personDao 의 범위를 제한
    val person: Person = getPerson()
    val personDao: PersonDao = getPersonDao()

    // 수행 결과를 반환 합니다.
    personDao.insert(person)
}

fun printAge(person: Person) = person.run {
    // person 을 수신객체로 변환하여 age 값을 사용
    print(age)
}

 

 

 

여러 범위 지정 함수 결합

위의 함수들을 결합 하는 코드를 작성하기 위한 원칙은 다음과 같다.

 

  • 원칙적으로 여러 함수를 중첩은 하지 않는 것이 좋다.
  • apply, run, with this가 생략 가능하고 이름도 다르게 지정할 수 없기 때문에 중첩될 경우 혼동하기 쉬워진다.

  • alsolet을 중첩할 경우, it을 사용하지 말고 명시적인 이름을 사용해 혼동을 줄인다.

  • Chaining이 가능한 경우 코드 가독성도 향상되기 때문에 사용하는 것이 좋다.

ex) chaning 코드

private fun insert(user: User) = SqlBuilder().apply {
    append("INSERT INTO user (email, name, age) VALUES ")
    append("(?", user.email)
    append(",?", user.name)
    append(",?)", user.age)
}.also {
    print("Executing SQL update: $it.")
}.run {
    jdbc.update(this) > 0
}

 

 

 

 

 

[참고한 사이트]

'Mobile > Kotlin' 카테고리의 다른 글

[Kotlin] Java와 Kotlin의 Covariance 차이  (0) 2020.03.26
[Kotlin] PECS 원칙  (0) 2020.03.26
[Kotlin] Covariance & Contravariance  (0) 2020.03.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함