[Kotlin] with vs run 명확한 차이점 톺아보기

H43RO·2021년 11월 23일
11

Kotlin 과 친해지기

목록 보기
18/18
post-thumbnail

옛날 옛적, 필자는 코틀린의 Scope Function 의 종류와 각각의 특징에 대해 포스팅을 한 적 있다. 그런데, 그 때 정말 글을 무책임하게 썼다는 생각이 문득 들었다. 왜냐하면 최근 본 면접에서 관련한 내용에 대해 물어보셨는데, 나름대로 잘 대답했다고 생각했으나 다시 제대로 공부할 필요가 있다는 피드백을 받았기 때문이다.

문제의 파트는 다음과 같다.

with 에 대해 설명할 때, run 과 특징상 별 차이가 없다며 입맛대로 골라쓰라는 몰상식한 발언을 해버렸다. 지금 생각해도 멍청하다

따라서, 오늘 포스팅은 이 둘의 엄밀한 차이에 대해 다뤄보고자 한다.

with

with 는 모양새를 보면 알 수 있듯, 일반 함수다. 파라미터로 직접 객체를 입력받고, 객체를 사용하기 위한 람다 블록을 받는다. 이렇게 생겼다.

fun <T, R> with(receiver: T, block: T.() -> R): R

이렇게 receiver 로 객체를 입력받으면, it 이나 this 등 키워드 없이 객체의 속성을 참조하거나 변경할 수 있다.

val person = Person("H43RO", 23)

with(person) {
    println(name)
    println(age)
    // 자신을 참조해야 하는 경우 this 키워드 사용
}

// H43RO
// 23

따라서, withnon-null 객체를 이용할 때만 사용할 수 있고 아무런 리턴을 할 수 없기 때문에 별다른 반환값이 필요하지 않을 때 사용한다.

즉, 객체의 함수나 속성을 여러 번 호출할 때 코드들을 그룹핑하는 용도로 활용할 수 있다.


run

run 은 두 가지 형태로 선언되어 있는데, 우선 첫 번째는 다음과 같다.

fun <T, R> T.run(block: T.() -> R): R

with 와 다른 점이라면, T 의 확장함수로 선언되어 있다는 점이다. 확장함수이기 때문에 with 와 다르게 Safe Call 을 붙인다면 null 객체가 들어와도 non-null 검사를 하고 실행할 수 있다.

val person = Person("H43RO", 23)
val ageNextYear = person.run {
    ++age  // Return
}

println("$ageNextYear")

// 24

또한, 마지막 실행문의 결과를 반환하게 된다.

따라서, 객체의 특성이나 메소드 등을 활용하여 어떤 값을 계산할 필요가 있거나, 여러 개의 지역변수 범위를 제한하고자 할 때 사용한다.

그리고 두 번째 run 선언 방식은 다음과 같다.

fun <R> run(block: () -> R): R

보이다시피 이것은 확장함수가 아니고, 블록에 입력값도 없다. 객체 속성을 이용하려는 상황에 사용되는 함수가 아니고, 어떤 객체를 생성하기 위한 실행문들을 하나로 묶어 리더빌리티를 높이는 역할을 수행한다.

val person = run {
    val name = "H43RO"
    val age = 23
    Person(name, age)  // Return
}

이러면, Person(name="H43RO", age=23) 객체가 person 에 담기게 된다.


알아본 것 처럼, withrun 은 엄밀히 따지면 차이가 분명하다. 선언 방식도 다르고, 그에 따라 용법의 차이가 있다.

참고자료

https://blog.yena.io/studynote/2020/04/15/Kotlin-Scope-Functions.html

profile
어려울수록 기본에 미치고 열광하라

3개의 댓글

comment-user-thumbnail
2022년 10월 18일

안녕하세요! 블로그 잘 보고 있습니다🙇‍♂️ 한가지 궁금한 점이 있어서 댓글 남기게 되었네요😃
with 는 아무런 리턴을 할 수 없다고 말씀 주셨는데, with 함수 시그니처를 보니 R 타입을 리턴하고 있네요!
제가 코틀린은 시작한지 얼마 안되서... 맞을지 모르겠지만
R 타입을 리턴하는 거면, 제네릭 타입 파라미터에 지정한 타입 반환받을 수 있는게 아닐까요??

1개의 답글