Kotlin-未捕获ClassCastException

时间:2018-09-19 10:40:40

标签: kotlin classcastexception

考虑以下功能

fun issueAssetOrThrow(): ResponseEntity<AssetResponseMessage> {
    val response = getOrThrow(controller.issueAsset(ASSET_REQUEST_MESSAGE))

    return try {
        response as ResponseEntity<AssetResponseMessage>
    } catch (classCastException: ClassCastException) {
        val errorResponse = response as ResponseEntity<ErrorResponseMessage>
        fail(errorResponse.body.error)
    }
}

在成功响应的情况下,getOrThrow(controller.issueAsset(ASSET_REQUEST_MESSAGE))将返回ResponseEntity<AssetResponseMessage>

如果响应失败,它将返回ResponseEntity<ErrorResponseMessage>,除非发生一些灾难性的失败,在这种情况下,它将引发异常。

由于此时响应的通用部分未知,因此函数应首先try将响应转换为ResponseEntity<AssetResponseMessage>

如果无法执行此强制类型转换,它将抛出ClassCastException,然后尝试将响应强制类型转换为ResponseEntity<ErrorResponseMessage>

然后应提取错误消息并与此消息一起fail,并抛出AssertionError

问题是我实际上得到了这个输出

  

java.lang.ClassCastException:   com.demo.api.messages.ErrorResponseMessage无法转换为   com.demo.api.messages.AssetResponseMessage

看来catch(classCastException: ClassCastException)被完全忽略了!为什么会这样?

2 个答案:

答案 0 :(得分:1)

泛型类型信息在运行时被擦除。话虽如此,以下内容:

response as ResponseEntity<AssetResponseMessage>

可能已经向您显示了编译器警告(未经检查的强制转换),并且实际上与您编写时相同:

response as ResponseEntity<*>

或再次使您感到困惑,即使您将AssetResponseMessage放在ResponseEntity内,也可以再次使用未经检查的强制转换:

response as ResponseEntity<ErrorResponseMessage>

这也是为什么我问那个特定的错误消息准确地放在哪里的原因。它不能扔在try内,也不能扔在catch内。因此,唯一缺少的部分是getOrThrow本身。

还请注意,您可能宁愿使用is并从智能强制转换中受益,而不是直接强制转换并希望使用ClassCastException(针对异常进行编程是一种反模式)。

以下是示例:

fun getOrThrow(issueAsset : Any /* I don't mind the actual type for now */) = when (issueAsset) {
    is AssetResponseMessage -> TODO("ResponseEntity<AssetResponseMessage> up to you")
    else -> TODO("error? up to you")
}

或者,您可以使用安全类型转换运算符(as?)。如果强制转换不成功,则安全类型转换运算符基本上会将值设置为null,例如:

 val sample = "test" as? Int
 // now sample has the type Int? and is actually null

注意:强制转换或安全转换为具有泛型类型的类将无法确保该对象实际上包含该泛型类型,例如someObj as List<Int>不会确保您实际上具有一个整数列表,而只会确保您仅具有一个List。只需记住:泛型类型信息会在运行时删除。

答案 1 :(得分:-1)

感谢lrcover建议as?,我现在已经提出了解决方案

private fun <TResult> responseOrFail(response: Any): ResponseEntity<TResult> {
    val errorResponse = response as? ResponseEntity<ErrorResponseMessage>

    if (errorResponse != null) {
        fail(errorResponse.body.error)
    }

    return response as ResponseEntity<TResult>
}

然后用我的方法

return responseOrFail(response)

现在我得到了想要的结果。