Kotlin에서 map, forEach 같은 고차 함수 내부에서 반복을 제어할 때 @label을 사용할 수 있다.
특히 람다 내부에서 특정 조건일 때 현재 iteration만 건너뛰고 다음 요소로 넘어가고 싶을 때 return@label을 사용한다.
공휴일 정책을 개발하면서 사용했던 Label Return 개념과 사용 방법을 정리해보려고 한다.
private fun extractNewHolidays (
existingDates: List<LocalDate>,
apiDataList: List<openApiResponse>
): List<Holiday> {
return apiDataList.mapNotNull { item ->
val data = LocalDate.parse(
item.data!!,
DateTimeFormatter.ofPattern("yyyyMMdd")
)
if (date in existingDates) {
// 이미 존재하는 날짜는 제외
}
item.dateName?.let {
Holiday (
name = it,
date = date
)
}
}
}
여기서 문제는 "이미 존재하는 날짜라면 해당 iteration만 건너뛰고 싶다"
이럴 때 return@mapNotNull을 사용할 수 있다.
private fun extractNewHolidays(
existingDates: List<LocalDate>,
apiDataList: List<HolidayXmlResponseItem>
): List<Holiday> {
return apiDataList.mapNotNull { item ->
val date = LocalDate.parse(
item.locdate!!,
DateTimeFormatter.ofPattern("yyyyMMdd")
)
if (date in existingDates) return@mapNotNull null
item.dateName?.let {
Holiday(
name = it,
date = date
)
}
}
}
여기서 핵심 코드는 이 부분이다.
if (date in existingDates) return@mapNotNull null
또한, mapNotNull은 null을 반환하면 결과 리스트에 포함되지 않기 때문에 결과적으로 이미 존재하는 날짜는 필터링되는 효과가 있다.
Kotlin의 람다는 non-local return이 가능하다.
즉, 람다 내부에서 return을 사용하면 람다를 포함하고 있는 외부 함수가 종료될 수 있다.
fun test() {
listOf(1, 2, 3).forEach {
if (it == 2) return
}
println("end')
}
이 코드의 실행 흐름은 다음과 같다.
1 -> 정상 실행2 -> return3 -> test() 함수 전체 종료즉, forEach만 종료되는 것이 아니라 외부 함수까지 종료된다.
그래서 이런 상황을 제어하기 위해 Label Return을 사용한다.
Kotlin에서는 2가지 방식으로 label을 사용할 수 있다.
람다에 직접 label을 지정하는 방식
fun test() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit
print(it)
}
print(" 명시적 label")
}
1245 명시적 label
람다를 호출한 함수 이름을 label로 사용하는 방식
fun test() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach
print(it)
}
print(" 묵시적 label")
}
1245 묵시적 label
| 표현 | 의미 |
|---|---|
| return | 외부 함수까지 종료 |
| return@forEach | 현재 람다 실행만 종료 |
| return@label | 지정한 label 람다 실행 종료 |
예를 들어 아래는 다음과 같은 의미를 가진다.
return @mapNotNull null
Kotlin에서 Label Return은 람다 내부의 흐름 제어를 명확하게 만들기 위해 매우 자주 사용된다.
특히 유용한 상황이 있는데...
forEach에서 특정 조건일 때 iteration 건너뛰기mapNotNull에서 특정 요소 제외하기처음 보면 낯설 수 있지만, return@함수이름 형태만 이해하면 비교적 쉽게 사용할 수 있다.