오늘은 내배캠 5주차 강의에 있는 scope function의 정의와 사용법, 사용시기 등을 정리해보려한다.
1. let - 람다의 결과를 반환 / it 사용
// 1. non-null 값에 대해서만 code block을 실행시켜야 할 때
var strNum = "10"
var result = if(strNum != null) Integer.parseInt(it) else null
var result = strNum?.let {
// 중괄호 안에서는 it으로 활용함
Integer.parseInt(it)
}
// 2. 하나 이상의 call chain 결과를 호출할 때 (출력 등)
val strings = listOf("APPLE", "CAR")
strings.map{ it.length }
.filter{ it > 3 }
.let(::println) // [5]
// 3. with 처럼 변수이름 반복을 제거할 때 (프로퍼티, 메서드를 많이 호출 해야할 때)
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
2. with - 람다의 결과를 반환 / this 사용 (with를 대신해서 null 안정성이 있는 run을 많이 사용함)
with도 let과 같이 람다의 결과를 반환 한다. 그럼 사용할 시기에는 어떤 차이가 있을까?
with은 null이 아닐 때만 사용하는 게 좋다.
// 1. 특정 객체의 변수이름 반복을 제거할 때 (프로퍼티, 메서드를 많이 호출 해야할 때)
data class Person(
var name: String = "",
var math: Int = 0,
var average: Int = 0
)
fun main() {
var person1 = Person()
// 기존의 프로퍼티 변경 코드
person1.name = "김탱"
person1.math = 85
person1.average = 95
println(person1) // Person(name=김탱, math=85, average=95)
// with를 사용해 프로퍼티를 수정하는 코드
with(person1) {
name = "김탱"
math = 40
average = 50
}
println(person1) // Person(name=김탱, math=40, average=50)
}
// 2. 특정(person) 객체를 다른(PersonDto) 객체로 변환해야 하는데
// 모듈간의 의존성에 의해 팩토리 함수 toClass()를 만들기 어려울 때
val newPersonObject = with(person){
PersonDto(
name = name, (우측 name에 this. 생략함)
age = age, (우측 age에 this. 생략함)
)
}
3. run - 람다의 결과를 반환 / this 사용
// 1. 객체에서 호출하는 경우 - 객체 구성 및 추가 로직(결과 계산 등)
fun main() {
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port") // result에 저장되는 값
}
// same code, written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}") // letResult에 저장되는 값
}
println(result) // same
println(letResult) // smae
// 같은 이유 : run과 let모두 람다식 결과를 반환하는 메서드이기 때문에
}
// 2. 일반적으로 사용하는 경우 - 지역적인 공간을 만들어 변수를 선언하고, 값을 정의할 수 있음
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
println(match.value)
}
4. apply - 객체 자신을 반환 / this 사용
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam) // Person(name=Adam, age=32, city=London)
5. also - 객체 자신을 반환 / it 사용
val numbers = mutableListOf("one", "two", "three")
numbers.also {
println("The list elements before adding new one: $it") // 새 요소를 추가하기 전의 목록 요소: [one, two, three]
}.add("four")
6. 객체 자체를 반환한다는 것과 람다의 값을 반환한다는 건 어떤 차이일까?
아래의 2개 예제를 보자~
// apply : 객체 자체를 반환한다. 즉, apply 앞의 MutableList 객체를 반환
val mlist = mutableListOf("one", "two", "three").apply { add("four") }
println(mlist) // [one, two, three, four]
// run : 람다를 반환한다. 즉, run 안의 add("four")의 값을 반환
val mlist1 = mutableListOf("one", "two", "three").run { add("four") }
println(mlist1) // true ( add의 메서드 반환타입이 boolean임 -> 제대로 추가되었다 라는 뜻인듯)
7. 사용시기 총 정리(공식문서)
- null을 허용하지 않는 객체에서 람다 실행: let
- 지역 범위에서 변수로 표현식 소개: let
- 객체 구성: apply
- 객체 구성 및 결과 계산: run
- 표현식이 필요한 명령문 실행: 비확장 run
- 추가 효과: also
- 객체에 대한 함수 호출 그룹화: with
8. 정리 후기
- apply : 특정 객체를 초기화하고, 그 객체를 반환해야할 때
- run : 객체를 초기화 한 뒤, 다른 값을 계산해서 반환해야할 때
- let : non-null 일 때, 람다식 실행을 해야할 때
- also : side-effect (추가 효과)
'Kotlin' 카테고리의 다른 글
[Kotlin in Action 4장] 정주행 (0) | 2024.03.20 |
---|---|
[Kotlin in Action 3장] 정주행 (1) | 2024.03.01 |
[Kotlin in Action 1, 2장] 정주행 시작 (0) | 2024.02.27 |
shuffle()와 shuffled()의 차이점 및 배열(Array), 리스트(List) 정리 (0) | 2024.02.19 |