안들로이드로 네트워크 작업을 하다 보면 이 Result클래스를 보곤 했습니다. 이름으로 봤을 때는 이게 대충 결과겠거니 하면서 여러 네트워크 Api의 양식서에 있는 것을 보며 넘어갔습니다. 이번 포스팅에서는 이 Result 클래스가 무엇인지 알아볼까 합니다.
Result class는 두 가지로 나눌 수 있을 것 같습니다.
- 사용자 지정 Result
- runCatching의 반환값으로 이용되는 Result
사용자 지정 Result
sealed class Result<out T>
class Success<out T>(val result: T): Result<T>()
class Failure(val throwable: Throwable): Result<Nothing>()
fun divide(a: Int, b: Int): Result<Int> {
return if (b != 0) {
Success(a / b)
} else {
Failure(IllegalArgumentException("Cannot divide by zero"))
}
}
fun printResult(result: Result<Int>) {
when (result) {
is Success -> println("Result: ${result.value}")
is Failure -> println("Error: ${result.throwable.message}")
}
}
fun main() {
val result1 = divide(10, 2)
val result2 = divide(10, 0)
printResult(result1) // Output: Result: 5
printResult(result2) // Output: Error: Cannot divide by zero
}
사용자 지정 Result 같은 경우 이러한 방식으로 사용됩니다. 위방식은 성공적으로 값을 반환하거나 오류를 반환하는데 도움을 줍니다.
fun divide(a: Int, b: Int): Pair<Boolean,Int> {
return if (b != 0) {
Pair(true,a/b)
} else {
Pair(false,0)
}
}
위방식은 Pair를 반환하여 값의 결과를 확인하는 것과 비슷할 수 있지만 Failure와 Success라는 클래스명을 이용하여 좀 더 명시적이면서 무슨 에러가 생긴 것인지 확인할 수 있습니다. 또한 Failure와 Success는 Result의 sealed 클래스이기 때문 when문을 사용하기 적절합니다. 만약 또 다른 상태를 차별화한 상태를 반환하고 싶은 경우 새롭게 클래스를 만들어줘 사용할 수 있기 때문에 유용합니다.
runCatching의 반환값으로 이용되는 Result
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/
코틀린은 1.3 버전부터 이러한 Reult클래스를 재공 합니다. 이러한 Reult클래스의 주요 기능은 runCatching의 반환값으로 이용된다는 것입니다.
@InlineOnly
@SinceKotlin("1.3")
public inline fun <R> runCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
@InlineOnly
@SinceKotlin("1.3")
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
runCatching() 함수는 이러한 구조의 함수입니다. block이라는 매개변수에 함수를 집어넣어 해당 부분에서 Exception이 생기는 경우 Result클래스 내부의 Failure클래스로 감싸진 예외 정보를 반환합니다.
fun divide(a: Int, b: Int): Int {
if (b == 0) {
throw IllegalArgumentException("Cannot divide by zero")
}
return a / b
}
fun main() {
val result1 = runCatching { divide(10, 2) }
val result2 = runCatching { divide(10, 0) }
if (result1.isSuccess) {
println("Result: ${result1.getOrNull()}") // Output: Result: 5
} else {
println("Error: ${result1.exceptionOrNull()?.message}")
}
if (result2.isSuccess) {
println("Result: ${result2.getOrNull()}")
} else {
println("Error: ${result2.exceptionOrNull()?.message}") // Output: Error: Cannot divide by zero
}
}
위처럼 runCatching()은 Result의 클래스의 인스턴스를 반환하며 코틀린 내부에 주어지는 함수로 더욱 안전하게 값들과 상태를 볼 수 있습니다.
결과적으로 2개의 Result클래스 모두 반환의 상태와 값을 담고 있습니다. 그렇기 때문에 위에서 말한 내크워크를 사용하는 경우 여러 예외가 생길수있기때문에 Result를 사용하는것 같습니다. 그렇지만 앞서 설명한 2개의 Result클래스는 서로 다른 용도로 사용해야합니다. 그이유는 runCatching()함수 내부의 try catch문 때문입니다. 이 try catch문 예외가 생길경우 그예외들을 잡아내지만 블록 내부에 코드를 배치하면 컴파일러가 할 수 있는 최적화가 제한되거나 예외는 예외적인 상황을 처리하기 위해서 만들어졌으므로 명시적인 테스트만큼 빠르게 동작하지 않습니다. 그렇기때문에 2개의 기능은 차별해서 사용하여야 합니다. 그 부분은
https://seedpotato.tistory.com/243
이 글을 본다면 더욱 자세히 알 수 있을 것입니다.
'코틀린 > 문법및 라이브러리' 카테고리의 다른 글
[Kotlin] 여러 종류의 반복문 (0) | 2023.08.04 |
---|---|
[Kotlin] 교집합(intersect), 합집합(union), 차집합(subtract) (0) | 2023.07.31 |
[Kotlin] Contract 로 스마트 케스팅 (0) | 2023.07.18 |
[Kotlin] 시퀀스(Sequence) 파보기 (0) | 2023.07.13 |
[Kotlin] 콜렉션(Collection) 파보기 (0) | 2023.07.12 |