Kotlin - ClassCastException 未被捕获
Kotlin - ClassCastException isn't being caught
考虑以下函数
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))
将 return ResponseEntity<AssetResponseMessage>
。
如果响应不成功,它将 return ResponseEntity<ErrorResponseMessage>
,除非出现灾难性故障,在这种情况下它会抛出异常。
由于此时响应的通用部分未知,函数应首先 try
将响应转换为 ResponseEntity<AssetResponseMessage>
。
如果无法执行此转换,它将抛出一个 ClassCastException
,然后尝试将响应转换为 ResponseEntity<ErrorResponseMessage>
。
然后它应该提取错误消息和 fail
这个消息,抛出一个 AssertionError
问题是我实际上得到了这个输出
java.lang.ClassCastException:
com.demo.api.messages.ErrorResponseMessage cannot be cast to
com.demo.api.messages.AssetResponseMessage
看起来 catch(classCastException: ClassCastException)
被完全忽略了!这是为什么?
感谢 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)
现在我得到了想要的结果。
通用类型信息在运行时被删除。话虽这么说,以下内容:
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
注意:转换或安全转换为具有通用类型的 class 将无法确保对象实际包含该通用类型,例如someObj as List<Int>
不会确保您实际上拥有一个整数列表,而只是确保您只有一个 List
。请记住:泛型类型信息在运行时被删除。
考虑以下函数
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))
将 return ResponseEntity<AssetResponseMessage>
。
如果响应不成功,它将 return ResponseEntity<ErrorResponseMessage>
,除非出现灾难性故障,在这种情况下它会抛出异常。
由于此时响应的通用部分未知,函数应首先 try
将响应转换为 ResponseEntity<AssetResponseMessage>
。
如果无法执行此转换,它将抛出一个 ClassCastException
,然后尝试将响应转换为 ResponseEntity<ErrorResponseMessage>
。
然后它应该提取错误消息和 fail
这个消息,抛出一个 AssertionError
问题是我实际上得到了这个输出
java.lang.ClassCastException: com.demo.api.messages.ErrorResponseMessage cannot be cast to com.demo.api.messages.AssetResponseMessage
看起来 catch(classCastException: ClassCastException)
被完全忽略了!这是为什么?
感谢 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)
现在我得到了想要的结果。
通用类型信息在运行时被删除。话虽这么说,以下内容:
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
注意:转换或安全转换为具有通用类型的 class 将无法确保对象实际包含该通用类型,例如someObj as List<Int>
不会确保您实际上拥有一个整数列表,而只是确保您只有一个 List
。请记住:泛型类型信息在运行时被删除。