Kotlin –用于功能异常处理的Try类型

Scala具有Try类型,可以在功能上处理异常。 我可以通过出色的使用这种类型
丹尼尔·韦斯海德(Daniel Westheide)的《新植物指南》(Scala)中的Scala指南 。 这篇文章将使用 Kotlin

背景

考虑一个简单的函数,该函数接受两个String,将它们转换为整数,然后将它们相除(基于scaladoc的样本
试试 ):

fun divide(dividend: String, divisor: String): Int {
     val num = dividend.toInt()
     val denom = divisor.toInt()
     return num / denom
 }

调用者有责任确保使用Java / Kotlin的异常处理机制正确处理从此实现传播的任何异常:

try {
    divide("5t", "4")
} catch (e: ArithmeticException) {
    println("Got an exception $e")
} catch (e: NumberFormatException) {
    println("Got an exception $e")
}

我使用“尝试”代码的目标是将“分隔”转换为如下所示的内容:

fun divideFn(dividend: String, divisor: String): Try<Int> {
    val num = Try { dividend.toInt() }
    val denom = Try { divisor.toInt() }
    return num.flatMap { n -> denom.map { d -> n / d } }
}

此“ divide”函数变体的调用者将没有异常可以通过try / catch块进行处理,而是将异常作为值返回给它,可以根据需要进行内省和操作。

val result = divideFn("5t", "4")
when(result) {
    is Success -> println("Got ${result.value}")
    is Failure -> println("An error : ${result.e}") 
}

Kotlin实施

“尝试”类型具有对应于“成功”路径或“失败”路径的两种实现,并通过以下方式实现为密封类:

sealed class Try<out T> {}

data class Success<out T>(val value: T) : Try<T>() {}

data class Failure<out T>(val e: Throwable) : Try<T>() {}

“成功”类型包含执行的成功结果,“失败”类型包含执行中引发的所有异常。

因此,现在,要添加一些肉类,我的第一个测试是根据以下明确的,出色的实现返回这些类型之一:

val trySuccessResult: Try<Int> = Try {
    4 / 2
}
assertThat(trySuccessResult.isSuccess()).isTrue()


val tryFailureResult: Try<Int> = Try {
    1 / 0
}
assertThat(tryFailureResult.isFailure()).isTrue()

这可以通过Kotlin中的“伴侣对象”来实现,类似于Java中的静态方法,它会基于lambda表达式的执行返回Success类型或Failure类型:

sealed class Try<out T> {
    ...    
    companion object {
        operator fun <T> invoke(body: () -> T): Try<T> {
            return try {
                Success(body())
            } catch (e: Exception) {
                Failure(e)
            }
        }
    }
    ...
}

现在,调用者具有“尝试”类型,他们可以使用像以前一样的“时间”表达式,或者使用委派的“ isSuccess”和“ isFailure”方法来检查它是“成功”类型还是“失败”类型。像这样的子类型:

sealed class Try<out T> {
    abstract fun isSuccess(): Boolean
    abstract fun isFailure(): Boolean
}

data class Success<out T>(val value: T) : Try<T>() {
    override fun isSuccess(): Boolean = true
    override fun isFailure(): Boolean = false
}

data class Failure<out T>(val e: Throwable) : Try<T>() {
    override fun isSuccess(): Boolean = false
    override fun isFailure(): Boolean = true
}

在失败的情况下,可以将默认值返回给调用方,例如在测试中:

val t1 = Try { 1 }

assertThat(t1.getOrElse(100)).isEqualTo(1)

val t2 = Try { "something" }
        .map { it.toInt() }
        .getOrElse(100)

assertThat(t2).isEqualTo(100)

通过委托给子类型再次实现:

sealed class Try<out T> {
  abstract fun get(): T
  abstract fun getOrElse(default: @UnsafeVariance T): T
  abstract fun orElse(default: Try<@UnsafeVariance T>): Try<T>
}

data class Success<out T>(val value: T) : Try<T>() {
    override fun getOrElse(default: @UnsafeVariance T): T = value
    override fun get() = value
    override fun orElse(default: Try<@UnsafeVariance T>): Try<T> = this
}

data class Failure<out T>(val e: Throwable) : Try<T>() {
    override fun getOrElse(default: @UnsafeVariance T): T = default
    override fun get(): T = throw e
    override fun orElse(default: Try<@UnsafeVariance T>): Try<T> = default
}

但是,返回“ Try”类型的最大优点是可以链接该类型的其他操作。

与地图和flatMap链接

“ map”操作传递了一个lambda表达式以某种形式转换值-甚至可能转换为其他类型:

val t1 = Try { 2 }

val t2 = t1.map({ it * 2 }).map { it.toString()}

assertThat(t2).isEqualTo(Success("4"))

在此,将数字加倍,然后转换为字符串。 如果最初的Try是“ Failure”,那么最终值将简单地按照此测试的行返回“ Failure”:

val t1 = Try {
    2 / 0
}

val t2 = t1.map({ it * 2 }).map { it * it }

assertThat(t2).isEqualTo(Failure<Int>((t2 as Failure).e))

实施“地图”非常简单:

sealed class Try<out T> {
    fun <U> map(f: (T) -> U): Try<U> {
        return when (this) {
            is Success -> Try {
                f(this.value)
            }
            is Failure -> this as Failure<U>
        }
    }
}

另一方面,flatmap接受一个lambda表达式,该表达式返回另一个“ Try”类型,并将结果展平为“ Try”类型,类似于该测试的内容:

val t1 = Try { 2 }

val t2 = t1
        .flatMap { i -> Try { i * 2 } }
        .flatMap { i -> Try { i.toString() } }

assertThat(t2).isEqualTo(Success("4"))

遵循以下内容,实现起来也很简单:

sealed class Try<out T> {
    fun <U> flatMap(f: (T) -> Try<U>): Try<U> {
        return when (this) {
            is Success -> f(this.value)
            is Failure -> this as Failure<U>
        }
    }
}

“ map”和“ flatMap”方法是这种类型的强大工具,允许将复杂的操作链接在一起,专注于快乐的道路。

结论

Try是一种功能强大的类型,允许对代码中的异常进行功能处理。 我一直在使用Kotlin可用稻草人实现在我的github回购这里 - https://github.com/bijukunjummen/kfun

翻译自: https://www.javacodegeeks.com/2017/12/kotlin-try-type-functional-exception-handling.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值