Kotlin - ClassCastException не перехватывается - PullRequest
0 голосов
/ 19 сентября 2018

Рассмотрим следующую функцию

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 голосов
/ 19 сентября 2018

Информация общего типа стирается во время выполнения.При этом, следующее:

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.Просто помните: информация общего типа стирается во время выполнения.

0 голосов
/ 19 сентября 2018

Спасибо 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)

И теперь у меня есть желаемый результат.

...