Перехват нескольких исключений в Java -8 - PullRequest
71 голосов
/ 09 января 2020

При испытании функции multi-catch , которую я нашел в моем методе m1(), все работает нормально, как и ожидалось.

Однако в m2() тот же код не компилируется , Я только что изменил синтаксис, чтобы уменьшить количество строк кода.

public class Main {

    public int m1(boolean bool) {
        try {
            if (bool) {
                throw new Excep1();
            }
            throw new Excep2();
            //This m1() is compiling  abs fine.
        } catch (Excep1 | Excep2 e) {
            return 0;
        }
    }

    public int m2(boolean b) {
        try {
            throw b ? new Excep1() : new Excep2();
            //This one is not compiling.
        } catch (Excep1 | Excep2 e) {
            return 0;
        }
    }

    private static interface I {
    }

    private static class Excep1 extends Exception implements I {
    }

    private static class Excep2 extends Exception implements I {
    }
}

Почему метод m2() не компилируется?

Ответы [ 3 ]

79 голосов
/ 09 января 2020

Тип выражения

b ? new Excep1() : new Excep2()

равен Exception, поскольку это общий супертип Excep1 и Excep2.

Однако вы не перехватываете Exception, поэтому компилятор жалуется на это.

Если вы поймаете Exception, он пройдет компиляцию:

public int m2(boolean b) {
    try {
        throw b ? new Excep1() : new Excep2();
    } catch (Exception e) {
        return 0;
    }
}

Я попытался найти запись JLS, которая объясняет тип условной троицы выражение в вашем примере.

Все, что я смог найти, это то, что это конкретное выражение является 15.25.3. Ссылочное условное выражение .

Я не совсем уверен, считается ли оно поли-выражением или автономным выражением. Я думаю, что это автономно (поскольку поли-выражения включают контекст присваивания или контекст вызова, и я не думаю, что оператор throw считается одним из них).

Для автономного выражения: «Если второе и третий операнд имеет один и тот же тип (который может быть нулевым), тогда это тип условного выражения. "

В вашем случае второй и третий операнды имеют три общих типа - Object , Throwable и Exception - тип выражения должен быть одним из двух последних, поскольку «выражение в выражении throw должно обозначать переменную или значение ссылочного типа, который можно назначить (§5.2) для тип Throwable. "

Похоже, что компилятор выбирает наиболее конкретный c общий тип (Exception), и поэтому catch (Exception e) решает ошибку компиляции.

Я также попытался заменить ваши два пользовательских исключения двумя подклассами IOException, и в этом случае catch (IOException e) устраняет ошибку компиляции.

22 голосов
/ 09 января 2020

Вы путаете компилятор с этой строкой:

throw b ? new Excep1() : new Excep2();

Компилятор видит, что результатом выражения (слева от броска) является общий суперкласс между Except1 и Except2, который это исключение, и поэтому эффективный тип, который вы бросаете, становится исключением. Оператор catch не может определить, что вы пытаетесь выбросить Excep1 или Except2.

4 голосов
/ 09 января 2020

Java ограничивает вас перехватом или объявлением всех типов исключений, которые может генерировать метод,

Он ищет общего родителя для обоих (/ всех) исключений и ожидает, что вы перехватите или объявите как броски, например если Excep1 расширяется Throwable, вам придется ловить также Throwable

. В первом случае Java уверен, что вы бросаете Excep1 или Excep2

...