Java 中的异常处理

日常开发中难免会遇到对异常的处理,我们先来看一段按照 Java 的方式进行的异常处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
RetrofitUtil.getInstance().callAppData(call, object : NetListener {
            override fun onSuccess(message: String) {
                Log.d(TAG, "onSuccess() called with: message = $message")

                val bean = try {
                    GsonUtils.fromJson<CollectionBean>(message, CollectionBean::class.java)
                } catch (e: Exception) {
                    null
                }

                // showUI
            }

            override fun onFailed(message: String?) {
                Log.d(TAG, "onFailed() called with: message = $message")
            }
        })

Kotlin 中的异常处理

这是一段 Kotlin 里面的代码,然后我们针对服务器返回的 message Json 进行 Bean 对象的转换,由于服务器返回的数据可能会和原初定义的不一样,所以我们在这里进行的 try catch 的处理。这是 Java 处理异常的方式, 但是我们已经使用 Kotlin 语言, 可以使用 Kotlin 提供的方式,会更加的简便,代码的可读性也会更好,看下是如何实现的,以及可以有哪些扩展:

 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
 RetrofitUtil.getInstance().callAppData(call, object : NetListener {
            override fun onSuccess(message: String) {
                Log.d(TAG, "onSuccess() called with: message = $message")
              
                val result = message.runCatching {
                    val bean = GsonUtils.fromJson<CollectionBean>(message, CollectionBean::class.java)
                    return@runCatching bean
                }.onSuccess {
                    Log.i(TAG, "onSuccess: ")
                }.onFailure {
                    Log.i(TAG, "onFailure: ")
                }

                val orNull = result.getOrNull()
                val defaultValue = result.getOrDefault(CollectionBean())

                val orThrow = result.getOrThrow()
                val result3 = result.getOrElse {
                    Log.i(TAG, "onFail: 异常了")
                }

            }

            override fun onFailed(message: String?) {
                Log.d(TAG, "onFailed() called with: message = $message")
            }
        })

在 Kotlin 中使用异常处理的方式是使用 runCatching 函数,这其实是一个高阶函数。这里先简单介绍一下这些 API 稍后看下源码具体是如何实现的这个部分。首先在 runCatching 高阶函数的代码块中,这个部分就已经进行了 try catch 包裹的处理,同时还实现了 onSuccess 和 onFailure 高阶函数,实际业务需求可以在这里做成功和失败的逻辑处理,这里我只是做了日志的打印。

runCatching 函数返回的是一个 Result 类,Result 类中,一共定义了如上述的 4 个方法:

  • 其中 getOrNull 返回的是 runCatching 中处理的结果,是一个可空类型的,比如这里我返回的是 CollectionBean 类
  • getOrDefault 返回的是一个不可空的类型,调用的时候需要传递默认值,为 null 的时候会返回默认值
  • getOrThrow 未发生的异常返回的是 runCatching 中处理的结果,如果 runCatching 中发生了异常,调用这个 API 会抛出异常
  • getOrElse 返回处理的结果,或者是异常自己处理

源码分析

1.runCatching 的实现

1
2
3
4
5
6
7
8
9
@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 是一个内联的高阶函数,返回了一个 Result 类,函数里面对回调出去的 lambda 表达式进行了 try catch 的处理。返回的内容使用 Result.success 和 Result.failure 进行的包裹

2.getOrNull

1
2
3
4
5
6
7
@InlineOnly
public inline fun getOrNull(): T? =
       when {
            isFailure -> null
            else -> value as T
       }
}

异常的情况返回 null ,要么就是返回 value, value 是 Result 的一个属性,也就是上面使用 Result.success(block()) 中返回的结果

3.getOrDefault

1
2
3
4
public inline fun <R, T : R> Result<T>.getOrDefault(defaultValue: R): R {
    if (isFailure) return defaultValue
    return value as T
}

如果是异常的情况返回默认值,要么返回的是 value

4.getOrElse

1
2
3
4
5
6
7
8
9
public inline fun <R, T : R> Result<T>.getOrElse(onFailure: (exception: Throwable) -> R): R {
    contract {
        callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
    }
    return when (val exception = exceptionOrNull()) {
        null -> value as T
        else -> onFailure(exception)
    }
}

先看看 exceptionOrNull 处理的是什么:

1
2
3
4
5
public fun exceptionOrNull(): Throwable? =
        when (value) {
            is Failure -> value.exception
            else -> null
        }

exceptionOrNull 返回的是: 如果是发生了异常返回异常类, 否则返回 null, 所以 getOrElse 里面返回的是: 异常情况下,返回异常类,否则返回处理的结果 value