티스토리 뷰
ㅅException이 Throw여부에 따라 각각 어떤 코드를 실행하고 싶을때가 있죠.
try-catch(-finally)로 구현하기 힘들때가 있습니다.
try-catch(-finally)는
Exception이 Throw되었을때의 처리(또는 throw여부에 관계없이 처리) 는 구현할 수 있어도 throw 되지 않았을때의 처리를 구현하기는 어렵습니다.
예를 들어 생각해봅시다.
일단 Exception이 throw 되는것을 생각하지 않아도 되는 케이스가 있습니다.
fun noException() {
val myValue =
getMyValue() // 값을 취득한다
.also {
onMySuccess(it) // 그 다음에 onMySuccess 를 호출한다
onMyFinally() // 마지막에 onMyFinally 를 호출한다
}
setMyValue(myValue)
}
여기서 getMyValue()에 예외처리를 한다고 생각해 봅시다.
Exception이 throw된 경우에는 onMyFailure 함수를 호출하여 임의의 값을 취득하도록 합시다
간단하게 구현하자면 이렇게 됩니다.
fun useTryCatch1() {
val myValue =
try {
getMyValue() // getMyValue()는 Exception을 throw할 가능성이 있다.
.also { onMySuccess(it) } // Exception이 Throw되지 않는다면 onMySuccess를 호출한다.
} catch (e: MyException) {
onMyFailure(e) // Exception이 Throw된다면 onMyFailure 를 호출하여, 임의의 값을 취득한다.
} finally {
onMyFinally() // 마지막으로 onMyFinally 를 호출한다.
}
setMyValue(myValue)
}
하지만 위와 같은 방식은 좋은 구현이 아닙니다. try의 내부에 관계없는 onMySuccess함수도 호출하고 있기때문입니다.
혹시 실수로 onMySuccess 가 Exception을 발생시켰을 경우에 무시되어 이슈를 발견하기 어렵게 만들 수 있습니다.
onMySuccess 를 try 구문의 밖으로 내보냅시다.
fun useTryCatch2() {
val myValue =
run {
try {
getMyValue()
} catch (e: Throwable) {
return@run onMyFailure(e)
}.also {
onMySuccess(it)
}
}.also {
onMyFinally()
}
setMyValue(myValue)
}
…너무 복잡해졌네요.
runCatching 함수를 써봅시다.
그러면 try-catch 대신에 runCatching 함수를 사용해봅시다.
fun useRunCatching() {
val myValue =
runCatching {
getMyValue()
}.fold(
onSuccess = { it.also { onMySuccess(it) } },
onFailure = { onMyFailure(it) }
).also {
onMyFinally()
}
setMyValue(myValue)
}
심플하죠.
try-catch-finally 에 Exception이 발생하지 않았을때의 처리구문이 추가된 것같은 알기 쉬운 구조입니다.
Result 클래스 — runCatching 함수의 반환형
runCatching 함수는 주어진 블록을 실행하여 Result 형태의 오브젝트를 반환합니다.
val result: Result<MyValue> = runCatching { MyValue() }
만약 블록내에서 throw 하더라도 runCatching 함수는 해당 Exception을 밖으로 throw하지 않습니다.
블록을 실행한 결과인 반환값이나 Exception을 runCatching 함수가 반환한 Result 오브젝트로부터 취득하는 것이 가능합니다
Result 클래스 맴버
블록의 실행이 성공하면 (블록내에서 예외처리가 발생하지 않았을때) isSuccess 변수를 확인하여 알 수 있습니다.
역으로 블록의 실행이 실패했다면 isFailure 변수를 통해 확인 할 수 있습니다.
if (result.isSuccess) { println("성공") }
if (result.isFailure) { println("실패") }
-
함수 getOrNull 와 exceptionOrNull
블록의 반환값을 얻기 위해선 getOrNull 함수를 사용합니다.
블록내에서 예외가 발생한 경우에는 null이 반환됩니다.
블록내에서 throw된 exception을 얻기 위해선 exceptionOrNull 함수를 사용합니다.
블록내에서 예외가 발생하지 않은 경우에는 null이 반환됩니다.
result.getOrNull()
?.also { println("반환값은 $it 입니다.") }
?: println("Exception이 발생했습니다.")
result.exceptionOrNull()
?.also { println("Exception은 $it 입니다.") }
?: println("반환값이 존재합니다.")
Result 클래스의 확장함수
Result 클래스의 맴버는 위에 서술한 4가지뿐입니다.
하지만 확장 함수가 다수 존재하고 있어서 매우 편리합니다.
- get계열 함수
get 계열 함수는 블록 실행이 성공했을때는 그 결과를 반환합니다. 실패했을때는 동작이 함수마다 다릅니다.
getOrThrow :
블록내에서 예외가 발생하면 그대로 throw합니다
getOrDefault :
실행이 실패하면 디폴트 값을 반환합니다.
getOrElse :
실행이 실패하면 지정된 코드를 실행합니다
val result = runCatching { "".toInt() }
try {
result.getOrThrow()
} catch (e: Exception) {
println("0") // > 0
}
result.getOrDefault(1).let { println(it) } // > 1
result.getOrElse { println(2) } // > 2
- on 계열 함수
onSuccess 함수블록은 runCatching 함수 블록의 실행이 성공했을때 실행됩니다
onFailure 함수블록은 runCatching 함수 블록의 실행이 실패했을때 실행됩니다
이 함수들은 리시버의 Result 오브젝트를 그대로 반환하기때문에 체인호출이 가능합니다.
result
.onSuccess { onMySuccess(it) }
.onFailure { onMyFailure(it) }
이 함수들은 inline 함수이기때문에 블록 내부에서 return 하거나 throw 할수 있습니다.
fun myFun() {
run {
result
.onSuccess { return@run } // 성공하면 run 블록에서 빠져나간다
.onFailure { throw Exception() } // 실패하면 myFun 함수로부터 Exception을 throw
}
}
-
map 계열 함수와 recover 계열 함수
아래와 같은 예외 클래스와 함수가 정의되어 있다.
class MyIntException : Exception()
class MyDoubleException : Exception()
fun throwMyIntException(): Int = throw MyIntException()
fun throwMyDoubleException(): Double = throw MyDoubleException()
- map계열 함수
map 함수와 mapCatching 함수는 runCatching 함수의 블록을 실행한 결과를 임의의 형태의 값으로 교환합니다.
runCatching { 3.14 }
.map { it.toInt() }
.onSuccess { println(it) } // > 3
runCatching { 3.14 }
.mapCatching { it.toInt() }
.onSuccess { println(it) } // > 3
다만, 교환 처리중에 예외가 발생한 경우의 동작이 다릅니다.
map 함수는 Exception을 밖으로 throw합니다.
mapCatching 는 반환값인 Result 가 Exception을 실패 반환값으로 갖게 됩니다.
try {
runCatching { 3.14 }
.map { throwMyIntException() }
.onSuccess { println(it) } // 이 람다식은 실행되지 않는다.
.onFailure { println(it) } // 이 람다식은 실행되지 않는다.
} catch (e: MyIntException) {
println(e) // > MyIntException
}
runCatching { 3.14 }
.mapCatching { throwMyIntException() }
.onFailure { println(it) } // > MyIntException
- recover계열 함수
함수 recover 와 recoverCatching 는 runCatching 함수의 블록의 실행이 실패했을때 성공으로 바꿉니다
runCatching { throwMyDoubleException() }
.recover { 3 }
.onSuccess { println(it) } // > 3
runCatching { throwMyDoubleException() }
.recoverCatching { 3 }
.onSuccess { println(it) } // > 3
다만 변환중에 예외가 발생한 경우의 동작이 다릅니다.
recover 함수는 Exception을 밖으로 throw합니다.
recoverCatching 는 반환값인 Result 가 Exception을 실패 반환값으로 갖게 됩니다.
try {
runCatching { throwMyDoubleException() }
.recover { throwMyIntException() }
.onSuccess { println(it) } // 이 람다식은 실행되지 않는다.
.onFailure { println(it) } // 이 람다식은 실행되지 않는다.
} catch (e: MyIntException) {
println(e) // > MyIntException
}
runCatching { throwMyDoubleException() }
.recoverCatching { throwMyIntException() }
.onFailure { println(it) } // > MyIntException
- fold함수
fold 함수는 runCatching 함수가 성공했을 경우에는 인수 onSuccess로 주어진 동작을 수행하고, 실패했을 경우에는 인수 onFailure에 주어진 동작을 실행합니다.
위에서 소개한 함수 onSuccess 와 onFailure 를 양쪽 다 호출하는 것은 비슷하지만, 한가지 큰 차이점은 반환값입니다.
fold 함수는 성공했을 경우와 실패했을 경우의 동작이 함께 같은 형태의 반환값을 갖도록 되어 있고 그것이 fold 함수의 반환값이 됩니다.
val double = runCatching { "".toInt() }.fold(
onSuccess = { it.toDouble() },
onFailure = { Double.NaN }
)
try-catch 식에 반환값을 사용하는 경우에는 이 함수를 사용하게 되겠네요
주의점
Result 는 함수의 반환값으로는 사용할 수 없다.
Result 는 함수의 반환값으로는 사용할 수 없습니다
fun getResult(): Result<Int> { // 컴파일 에러가 발생합니다.
return runCatching { 1 }
}
Result 는 함수내에서 처리합시다.
'프로그래밍 > ANDROID' 카테고리의 다른 글
[번역] Jetpack Compose Composable함수의 인수를 별도 모듈 클래스로 사용할때의 주의점 (0) | 2023.04.20 |
---|---|
[번역] 누구나 5분이면 이해하는 DI ( Dependency Injection ) (0) | 2018.06.06 |
Android Architecture Components (AAC) 를 Realm과 접목시켜보자 (0) | 2017.11.23 |
[번역] [Qiita] RxJava2 에서 변경점 요약 (0) | 2017.06.09 |
[Android/IOS] Firebase Cloud Message (FCM) 유의점 정리 (0) | 2017.02.27 |
- Total
- Today
- Yesterday
- 청계천 맛집
- 을지로3가
- 여행
- 덴뿌라
- mobx
- 을지면옥
- 리액트 네이티브
- 리액트네이티브
- 일드
- 필동면옥
- 야키니쿠
- 일본여행
- 맛집
- 도쿄맛집
- 수요미식회
- 중쇄를찍자
- 평양냉면
- 안드로이드
- 평양면옥
- android
- 브이로그
- Qiita
- react native
- 편육
- 리액트
- Redux
- 우래옥
- 쿠로키 하루
- observable
- 도쿄
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |