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