티스토리 뷰

kotlin을 쓰면서 scope 함수를 구분 없이 막 쓰는 것 같아서 용도에 따라 함수를 구분해 쓸 수 있도록 좀 정리를 해봤다.

 

Kotlin scope 함수

kotlin에서는 let, run, with, apply, also와 같은 scope 함수를 제공한다.

이 함수들은 람다식과 같은 형식으로 코드 블록으로 객체에 원하는 기능을 수행할 수 있게 해준다.

예를 들면 아래와 같다.

testUser.apply {
    name = "test"
    age = 30
}

 

 

apply라는 scope 함수를 쓰지 않으면 아래와 같이 풀어서 써야 하는 코드였다.

apply를 통해 코드 블럭으로 묶어서 아래와 같이 testUser.XXX를 쓸 필요가 없어졌으며 코드 블럭 안으로 딱 묶였다.

testUser.name = "test"
testUser.age = 30

 

 

이와 같이 코드 블럭으로 묶어주는 기능을 하는 scope 함수는 let, run, with, apply, also가 존재하며 각각의 함수는 비슷하면서도 용도와 return 값이 다르다.

 

 

let

제일 남발하기 좋은 scope 함수라고 생각한다.

특히 Object?.let{}을 통해서 null 값이 아닐 경우에 object 값을 활용해야 하는 경우 사용했다.

let은 코드 블럭 안에서 해당 object를 it을 통해 사용할 수 있다. 그리고 return 값은 코드 블럭 제일 마지막 값이다.

예를 들어 아래와 같이 사용할 수 있다. 아래 코드에서는 testUser 값이 null이 아닐 경우 그 안에 들어 있는 name, age를 출력하고 age를 따로 myAge에 저장하고 있다. 만일 testUser가 null일 경우 ?(엘비스 연산자)가 null을 반환하므로 myAge에는 null이 저장된다. 

val myAge = testUser?.let {
    println("My name is ${it.name}. I'm ${it.age} years old.")
    it.age
}

 

 

run

객체의 구성을 변경하고 별도의 결과를 도출해야할 때 사용한다.

run은 코드블럭 안에서 해당 object를 this를 통해 사용할 수 있다. 그리고 return 값은 코드 블럭 제일 마지막 값이다.

어찌보면 let과 진짜 비슷해보인다. 다른 점은 it과 this인데 this의 경우는 생략이 가능해서 코드 블럭 내에서 this를 생략하고 해당 객체의 구성을 변경할 수 있다는 것이다. 그래서 객체의 구성을 변경하는데는 let보다 편한 느낌이 있다.

 

예를 들어 아래와 같이 코드를 짰을 경우 "My name is test. 40 years old."라는 문구가 출력되고 newAge에는 40이 저장된다. testUser의 age 또한 40으로 변경 되어 저장된다.

val testUser = TestUser(
	name = "test",
	age = 30
)

val newAge = testUser.run {
	age = 40
	println("My name is $name. $age years old.")
	age
}

 

with

with은 객체에서 쓸 수 있는 함수는 아니다.

with은 따로 쓰는 scope 함수로, 인자로 받은 값에 대해 기능을 수행할 수 있다.

코드 블럭 내부에서 this를 사용하며 return 값은 코드 블럭 마지막 값이다.

 

예를 들어 아래와 같이 특정 함수의 결과 값을 저장해 그 값으로 무언가 로직을 수행한다고 하자.

나는 함수의 결과 값이 다 필요하지 않은데 활용하기 위해 변수를 생성해 저장해 사용해야 한다.

(map을 이용할 경우 코드가 간결해지긴 하겠지만 with 설명을 위해 풀어써본다.)

class MyTest {
    @Test
    fun test() {
    	// 과일 이름과 가격 출력하기 위해서 아래와 같이 변수 선언 필요
        val fruitsWithPrice = getFruitsWithPrice()
        
        for (fruitWithPrice in fruitsWithPrice) {
            println("${fruitWithPrice.key} price is ${fruitWithPrice.value}")
        }
    }

    private fun getFruitsWithPrice(): Map<String, Int> {
        return mapOf(
            "apple" to 1000,
            "banana" to 2000
        )
    }
}

 

나는 그저 getFruitsWithPrice()로 가져온 과일 이름과 가격을 출력하고 싶을 뿐인데 필요 없는 변수인 fruitsWithPrice를 선언해야 한다.

이럴 때 아래와 같이 with 함수를 이용하면 불필요한 변수인 fruitsWithPrice는 선언하지 않아도 되고 getFruitsWithPrice()로 가져온 값은 with 코드 블럭 내에서만 존재하게 된다.

    @Test
    fun test2() {
        with(getFruitsWithPrice()) {
            for (fruitWithPrice in this) {
                println("${fruitWithPrice.key} price is ${fruitWithPrice.value}")
            }
        }
    }

 

 

apply

apply는 해당 객체의 구성을 변경해야할 때 사용한다.

apply은 코드 블럭 안에서 object를 this를 통해 사용할 수 있으며 return 값은 해당 객체 자체이다.

즉, apply 코드 블럭 내에서 적용된 것은 return 값으로 나온 객체에도 적용되어 있는 것이다.

 

예를 들어 아래와 같이 testUser를 만들어 apply를 통해 name, age를 변경해서 newTestUser에 저장했을 경우 testUser와 newTestUser의 name은 test2, age는 35가 되어 있다.

val testUser = TestUser(
	name = "test",
	age = 30
)

val newTestUser = testUser.apply {
	name = "test2"
	age = 35
}

 

 

also

also는 코드 내부에서 it을 사용하며 return 값은 객체 그 자체이다.

무언가 객체에 대해 추가적인 동작을 할 때 사용한다고 한다.

개발하면서 제일 안 썼던 scope 함수였다.

testUser.also { println("My name is ${it.name}. ${it.age} years old." }

 

 

* 참고로 아래 코드 블럭 내부에서 객체의 값을 변경하기 위해서는 변경하고자 하는 값이 가변 변수(var)여야 한다. 불변 변수(val)일 경우에는 변경이 불가하다.

 

 

맨날 생각 없이 let만 썼는데 이렇게 정리를 해보니 각자 return 값도 다르고 내부에서 object를 사용하는 방법도 달랐다.

?.let 처럼 객체가 null이 아닐 경우 해야하는 로직을 짜는 것도 있지만 apply처럼 객체의 구성을 변경하기 편한 것도 있어 상황에 따라 적절하게 사용하면 좋을 것 같다.

코드를 줄이는 것뿐만 아니라 코드 블럭으로 딱 묶어줘서 구분을 해주는 이점도 있는 것 같다.

 

 

 

 

 

✋ Kotlin Scope functions

https://kotlinlang.org/docs/scope-functions.html

 

Scope functions | Kotlin

 

kotlinlang.org

 

 

 

 

반응형

'Kotlin' 카테고리의 다른 글

coroutine(코루틴)이란  (1) 2024.03.03
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함