Исключение уже перехвачено другой ошибкой исключения

Я экспериментировал с клиентской библиотекой Apache HTTP в Eclipse.

<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.3.1"/>  

и следующий фрагмент кода выдает проверенное исключение и требует обработки.

 HttpResponse response = httpClient.execute(httprequest);

Затмение дает 3 предложения

  1. Добавить выбрасывает исключение - throws ClientProtocolException, IOException (отлично работает)

  2. Окружить попыткой поймать -

    try {
        HttpResponse response = httpClient.execute(httprequest);
    }
    catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    

    (тоже работает нормально)

  3. Окружите с помощью try/multicatch

    try {
        HttpResponse response = httpClient.execute(httprequest);
    }
    catch (ClientProtocolException | IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    

3 вариант дает ошибку

Исключение ClientProtocolException уже перехвачено альтернативным IOException.

Я видел исходный код для ClientProtocolException и это IOException. Насколько я понимаю, при перехвате нескольких исключений мы можем поймать более общее исключение ниже более конкретного. Итак, мы не можем поймать ClientProtocolException после того, как поймали IOException.

Так почему же это происходит в multi try-catch? И если это не должно работать, почему Eclipse дает это предложение на 1-м месте?


person Aniket Thakur    schedule 27.09.2014    source источник


Ответы (3)


Код не должен компилироваться в соответствии с JLS. :

Это ошибка времени компиляции, если объединение типов содержит две альтернативы Di и Dj (i ≠ j), где Di является подтипом Dj (§4.10.2).

Просматривая список ошибок Eclipse, вы найдете несколько, связанных с предложениями по быстрому исправлению множественных уловок. Это похоже на Ошибка 388724.

httpClient.execute генерирует оба исключения, когда нужно только IOException, что является случаем, который разработчики Eclipse не учитывали при тестировании.

person fgb    schedule 27.09.2014

Нужно взглянуть на байт-код, сгенерированный javac, чтобы понять, почему возникает ошибка компилятора.

Я изменил ваш код и добавил RuntimeException, чтобы он компилировал и анализировал сгенерированный байт-код.

Следующий исходный код

try {
    HttpResponse response = httpClient.execute(request);
} catch (ClientProtocolException | RuntimeException e) {
    System.out.println("ClientProtocolException and RuntimeException");
} catch (IOException e) {
    System.out.println("IOException");
}

приведет к этому байтовому коду, подобному этому

TRYCATCHBLOCK L0 L1 L2 org/apache/http/client/ClientProtocolException
TRYCATCHBLOCK L0 L1 L2 java/lang/RuntimeException
TRYCATCHBLOCK L0 L1 L3 java/io/IOException
...
L2
 FRAME SAME1 java/lang/Exception
 ASTORE 1
L5
  LINENUMBER 18 L5
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  LDC "ClientProtocolException and RuntimeException"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
  GOTO L4
L3
  LINENUMBER 19 L3
  FRAME SAME1 java/io/IOException
  ASTORE 1
L7
  LINENUMBER 20 L7
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  LDC "IOException"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L4
...

Как видите, компилятор просто генерирует запись таблицы исключений (TRYCATCHBLOCK), указывающая на один и тот же байтовый код (L2) в случае ClientProtocolException или RuntimeException.

Итак, что произойдет, если вы попытаетесь сделать это

catch (ClientProtocolException | IOException e)

Тогда компилятор должен был бы сгенерировать две записи таблицы исключений, которые указывают на один и тот же байтовый код для выполнения, и они имеют общую иерархию (один является подклассом другого). Это означает, что если исключение перехвачено и jvm пытается определить следующий байтовый код для выполнения, две записи в таблице исключений будут совпадать. Итак, компилятор выдает вам ошибку: The exception ClientProtocolException is already caught by the alternative IOException.

Просто удалите ClientProtocolException, потому что ClientProtocolException это IOException.

person René Link    schedule 27.09.2014

Что касается опции try/multicatch № 3: ClientProtocolException является подклассом IOException. Поскольку вам нужно одинаковое поведение блока перехвата для обоих, нет необходимости явно перехватывать исключения типа подкласса. Любые такие выброшенные исключения будут перехватываться суперклассом IOException.

На этот вопрос также был дан ответ здесь.

person fwc    schedule 11.01.2016