Как отловить много исключений одновременно в Котлине

87
try { 

} catch (ex: MyException1, MyException2 ) {
    logger.warn("", ex)
}

или же

try { 

} catch (ex: MyException1 | MyException2 ) {
    logger.warn("", ex)
}

В результате ошибка компиляции: Unresolved reference: MyException2 .

Как я могу поймать много исключений одновременно на Kotlin?

Ant20
источник

Ответы:

100

Обновление: проголосуйте за следующую проблему KT-7128, если вы хотите, чтобы эта функция появилась в Kotlin. Спасибо @Cristan

Согласно этой теме, эта функция в настоящий момент не поддерживается.

абреслав - Команда JetBrains

На данный момент нет, но это на столе

Однако вы можете имитировать мульти-улов:

try {
    // do some work
} catch (ex: Exception) {
    when(ex) {
        is IllegalAccessException, is IndexOutOfBoundsException -> {
            // handle those above
        }
        else -> throw ex
    }
}
Минсол
источник
2
Я копирую pdvriezeответ здесь: This certainly works, but is slightly less efficient as the caught exception is explicit to the jvm (so a non-processed exception will not be caught and rethrown which would be the corollary of your solution)
solidak
1
@IARI Предложение elseповторно генерирует нежелательное исключение.
miensol
2
@IARI Сохранена исходная трассировка стека
miensol
2
Даже если отбросить аргументы элегантности и уродства в сторону, Kotlin (который претендует на краткость) на самом деле вдвое более многословен, чем Java, в этом случае
Phileo99
2
Об этом
сообщает Detekt,
8

Добавить в миенсол : хотя multi-catch в Kotlin еще не поддерживается, следует упомянуть и другие альтернативы.

Помимо try-catch-whenметода, вы также можете реализовать метод имитации множественного улова. Вот один вариант:

fun (() -> Unit).catch(vararg exceptions: KClass<out Throwable>, catchBlock: (Throwable) -> Unit) {
    try { 
        this() 
    } catch (e: Throwable) {
        if (e::class in exceptions) catchBlock(e) else throw e
    }
}

И в использовании это будет выглядеть так:

fun main(args: Array<String>) {
    // ...
    {
        println("Hello") // some code that could throw an exception

    }.catch(IOException::class, IllegalAccessException::class) {
        // Handle the exception
    }
}

Вы захотите использовать функцию для создания лямбды, а не использовать исходную лямбду, как показано выше (в противном случае вы довольно быстро столкнетесь с "MANY_LAMBDA_EXPRESSION_ARGUMENTS" и другими проблемами). Что-то вроде fun attempt(block: () -> Unit) = blockбы сработало.

Конечно, вы можете захотеть связать объекты вместо лямбда-выражений, чтобы составить вашу логику более элегантно или вести себя иначе, чем простой старый try-catch.

Я бы рекомендовал использовать этот подход вместо miensol , только если вы добавляете некоторую специализацию . Для простых многократных ловушек whenпростейшим решением является выражение.

Аро
источник
Если я правильно понимаю, вы передаете классы в своем catch, но param exceptionsпринимает объекты.
nllsdfx
ты классный парень @aro, спасибо за эту альтернативу
mochadwi
Хорошая альтернатива, спасибо Аро :) Лучше, чем ничего. Тем не менее, я надеюсь, что они
дойдут
0

Пример от aro очень хорош, но если есть наследования, он не будет работать, как в Java.

Ваш ответ вдохновил меня написать для этого функцию расширения. Чтобы также разрешить унаследованные классы, вы должны проверять их instanceвместо прямого сравнения.

inline fun multiCatch(runThis: () -> Unit, catchBlock: (Throwable) -> Unit, vararg exceptions: KClass<out Throwable>) {
try {
    runThis()
} catch (exception: Exception) {
    val contains = exceptions.find {
        it.isInstance(exception)
    }
    if (contains != null) catchBlock(exception)
    else throw exception
}}

Чтобы узнать, как использовать, вы можете заглянуть в мою библиотеку на GitHub здесь

Марк Ковальски
источник
В чем причина «-1»? try {...} catch (X | Y e) {...} в Java также проверяет наследование. Этот ответ имитирует поведение Java, он более удобен, например, для перехвата IOException с множеством разных подтипов.
Дмитрий Овчинников