ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코틀린을 다루는 기술 7장 Result
    카테고리 없음 2020. 7. 25. 10:52

    Option 만 쓰자니 정보의 제공에 한계가 있고,

    Either<A,B> 를 쓰자니 결과의 성공, 실패라는 Context를 담기에는 부족한 면이 있어서 나온 것이

    Result 이다.

     

    Result는 Success, Failure, Empty 3가지의 구현체를 제공하는데

    Failure, Empty의 차이는 

     

    Failure는 명시적인 실패의 이유를 담아서 리턴하고자 할 때,

    Empty는 정상적인 케이스가 아니지만 없는 건처럼 취급하고자 할 때 사용한다. 

    sealed class Result<out A> : Serializable {
        abstract fun <B> map(f: (A) -> B): Result<B>
        abstract fun <B> flatMap(f: (A) -> Result<B>): Result<B>
        
        abstract fun mapFailure(message: String): Result<A>
        
        abstract fun forEach(
            onSuccess: (A) -> Unit = {},
            onFailure: (RuntimeException) -> Unit = {},
            onEmpty: () -> Unit = {}
        )
    
        fun getOrElse(defaultValue: @UnsafeVariance A): A = when (this) {
            is Success -> this.value
            else -> defaultValue
        }
    
        fun getOrElse(defaultValue: () -> @UnsafeVariance A): A = when (this) {
            is Success -> this.value
            else -> defaultValue()
        }
    
        fun orElse(defaultValue: () -> Result<@UnsafeVariance A>): Result<A> = when (this) {
            is Success -> this
            else -> try {
                defaultValue()
            } catch (e: RuntimeException) {
                Failure<A>(e)
            } catch (e: Exception) {
                Failure<A>(RuntimeException(e))
            }
        }
    
        fun filter(p: (A) -> Boolean): Result<A> =
            filter("Condition not matched", p)
    
        fun filter(message: String, p: (A) -> Boolean): Result<A> = flatMap {
            if (p(it)) this
            else Result.failure(message)
        }
    
        fun exists(p: (A) -> Boolean): Boolean = map(p).getOrElse(false)
    
        internal object Empty : Result<Nothing>() {
            override fun <B> map(f: (Nothing) -> B): Result<B> = Empty
            override fun <B> flatMap(f: (Nothing) -> Result<B>): Result<B> = Empty
            override fun mapFailure(message: String): Result<Nothing> = Empty
            override fun forEach(
                onSuccess: (Nothing) -> Unit,
                onFailure: (RuntimeException) -> Unit,
                onEmpty: () -> Unit
            ) = onEmpty()
    
            override fun toString(): String = "Empty"
        }
    
        internal class Failure<out A>(private val exception: RuntimeException) : Result<A>() {
            override fun <B> map(f: (A) -> B): Result<B> = Failure(exception)
            override fun <B> flatMap(f: (A) -> Result<B>): Result<B> = Failure(exception)
            override fun mapFailure(message: String): Result<A> = Failure(RuntimeException(message))
            override fun forEach(
                onSuccess: (A) -> Unit,
                onFailure: (RuntimeException) -> Unit,
                onEmpty: () -> Unit
            ) = onFailure(exception)
    
            override fun toString(): String = "Failure(${exception.message})"
        }
    
        internal class Success<out A>(internal val value: A) : Result<A>() {
            override fun <B> map(f: (A) -> B): Result<B> = try {
                Success(f(value))
            } catch (e: RuntimeException) {
                Failure(e)
            } catch (e: Exception) {
                Failure(RuntimeException(e))
            }
    
            override fun <B> flatMap(f: (A) -> Result<B>): Result<B> = try {
                f(value)
            } catch (e: RuntimeException) {
                Failure(e)
            } catch (e: Exception) {
                Failure(RuntimeException(e))
            }
    
            override fun mapFailure(message: String): Result<A> = this
            override fun forEach(
                onSuccess: (A) -> Unit,
                onFailure: (RuntimeException) -> Unit,
                onEmpty: () -> Unit
            ) = onSuccess(value)
    
            override fun toString(): String = "Success($value)"
        }
    
        companion object {
            operator fun <A> invoke(a: A? = null): Result<A> = when (a) {
                null -> Failure(NullPointerException())
                else -> Success(a)
            }
    
            operator fun <A> invoke(): Result<A> = Empty
    
            fun <A> failure(message: String): Result<A> = Failure(IllegalStateException(message))
            fun <A> failure(exception: RuntimeException): Result<A> = Failure(exception)
            fun <A> failure(exception: Exception): Result<A> = Failure(IllegalStateException(exception))
        }
    }

     

     

    댓글

Designed by Tistory.