原创

Kotlin使用throw抛出异常

Kotlin允许程序自行抛出异常,自行抛出异常使用throw语句来完成。

一、抛出异常

如果需要字啊程序中自行抛出异常,则应使用throw语句。throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。

throw  ExceptionInstance

fun main(args: Array<String>) {
    //无论该异常在Java中是否为checked异常
    //在Kotlin中该异常都不是checked异常
    throwChecked(-3)
    throwRuntime(3)
}

fun throwChecked(a: Int) {
    if (a > 0) {
        //自行抛出普通异常,在Kotlin中也不是checked异常
        //该代码不必处于try块中
        throw Exception("a的值大于0,不符合要求")
    }
}

fun throwRuntime(a: Int) {
    if (a > 0) {
        throw RuntimeException("a的值大于0,不符合要求")
    }
}

输出结果:

Exception in thread "main" java.lang.RuntimeException: a的值大于0,不符合要求
	at 0708.ThrowTestKt.throwRuntime(ThrowTest.kt:20)
	at 0708.ThrowTestKt.main(ThrowTest.kt:7)

二、自定义异常类

通常情况下,程序很少会自行抛出系统异常,因为异常类的类名通常也包含了该异常的有用信息。

用户自定义异常都应该继承Exception基类,定义异常类时通常需要提供两个构造器:一个是无参数的构造器,另一个是带一个字符串参数的构造器,这个字符串将作为该异常对象的描述信息。

下面创建了一个自定义异常类:

class AuctionException : Exception {

    //无参数的构造器
    constructor()

    //带一个字符串参数的构造器
    constructor(msg: String) : super(msg) {}

}

三、catch和throw同时使用

当一个异常出现时,单靠某个方法无法完全处理该异常,必须通过几个方法协作才可完全处理该异常。

为了实现通过多个方法协作处理同一个异常的情形,可以在catch块中结合throw语句来完成。


public class AuctionTest {
    var initPrice: Double = 30.0
    fun bid(bidPrice: String) {
        var d: Double
        try {
            d = bidPrice.toDouble()
        } catch (e: Exception) {
            e.printStackTrace()
            //再次抛出自定义异常
            throw  AuctionException("竞拍价必须是数值,不能包含其他字符!")
        }
        if (initPrice > d) {
            throw  AuctionException("竞拍价比起拍价低,不允许竞拍!")
        }
        initPrice = d
    }
}

fun main(args: Array<String>) {
    val at = AuctionTest()
    try {
        at.bid("df")
    } catch (ae: AuctionException) {
        //再次捕获到bid()方法中的异常,并对该异常进行处理
        println(ae.message)
    }
}

输出结果:

java.lang.NumberFormatException: For input string: "df"
	at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
	at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.base/java.lang.Double.parseDouble(Double.java:543)
	at 0708.AuctionTest.bid(ActionTest.kt:8)
	at 0708.ActionTestKt.main(ActionTest.kt:24)
竞拍价必须是数值,不能包含其他字符!

catch和throw结合使用在大型企业级应用中非常常用。

  1. 应用后台需要通过日志来记录异常发生的详细情况;
  2. 应用还需要根据异常向应用使用者传达某种指示。

四、异常链

企业级应用常常有严格的分层关系,层与层之间有非常清晰的划分,上层功能的实现严格依赖于下层的API,也不会跨层访问。

把底层的原始异常直接传给用户是一种不负责任的表现。通常的做法是:程序先捕获原始异常,然后抛出一个新的业务异常,新的业务异常中包含了对用户的提示信息。这种处理方式被称为异常转译

捕获一个异常,然后抛出另一个异常,并把原始异常信息保存下来,这是典型的链式处理,也被称为“异常链”。

Kotlin的Throwable类及其子类在构造器汇总都可以接收一个cause对象作为参数。这个cause就用来表示原始异常,这样可以把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。

五、throw语句的表达式

Kotlin的throw语句也是表达式,throw表达式的类型是Nothing类型。

class User(var name: String? = null, var pass: String? = null)

fun main(args: Array<String>) {
    val user = User()
    //在Elvis表达式中使用throw表达式
    //throw表达式表示程序出现异常,不会真正对变量赋值
    val th: String = user.name ?: throw NullPointerException("目标对象不能为null")
    println(th)
}

输出结果:

Exception in thread "main" java.lang.NullPointerException: 目标对象不能为null
	at 0708.ThrowExprKt.main(ThrowExpr.kt:9)

Nothing类型没有值,而是用于标记永远无法“真正”返回的表达式,因此程序不会获取表达式的值。

学海无涯苦作舟

我的微信公众号.jpg

基本语法
  • 作者:HunterArley (联系作者)
  • 发表时间:2019-11-27 14:32
  • 版权声明:本网站部分内容转载于合作站点或其他站点,但都会注明作/译者和原出处。如有不妥之处,敬请指出。
  • 公众号转载:请在文末添加作者公众号二维码