Определение типа исключения multicatch во время компиляции

Я построил что-то, чего не очень понимаю — я не знаю, как это работает. Я ознакомился с этой статьей с объяснением multicatch .

Рассмотрим эти два исключения и код:

public class MyException1 extends Exception {
  // constructors, etc
  String getCustomValue();
}

public class MyException2 extends Exception {
  // constructors, etc
  String getCustomValue() { return "foo"; }
}

try {
  //...
} catch (MyException1|MyException2 e) {
  e.getCustomValue(); // won't work, as I expected
}

Я не смогу вызвать getCustomValue(), хотя метод тот же, потому что внутри Java вышеприведенный try/catch должен на самом деле преобразовывать MyException1/2 в Exception (именно так я понял документы).

Однако, если я представлю такой интерфейс:

public interface CustomValueGetter {
  String getCustomValue();
}

public class MyException1 extends Exception implements CustomValueGetter /*...*/
public class MyException2 extends Exception implements CustomValueGetter /*...*/

и добавить его в оба исключения, Java действительно позволяет мне использовать этот метод. И тогда вызов этого действителен:

try {
  //...
} catch (MyException1|MyException2 e) {
  e.getCustomValue(); // does work
}

Короче, мой вопрос: что здесь происходит на самом деле: (MyException1|MyException2 e).

Что такое e?

  • Выбран ли ближайший суперкласс в качестве типа e? Этот вопрос касается это и это, предположительно, ответ. Если да, то почему интерфейс CustomValueGetter «видим» при доступе к e? Этого не должно быть, если в моем случае e является Exception.

  • А если нет, то если реальный класс либо MyException1, либо MyException2, почему я просто не могу вызвать один и тот же метод, доступный для обоих этих классов?

  • Является ли e экземпляром динамически сгенерированного класса, который реализует все общие интерфейсы обоих исключений и относится к ближайшему общему типу суперкласса?


person Dariusz    schedule 27.08.2015    source источник


Ответы (3)


Как сказал Ischuetze, e ищет класс или интерфейс, которые разделяют оба исключения. В вашем первом примере не удается найти класс совместного использования, несмотря на класс Exception, поэтому он может использовать только предоставленные им методы.

Изменение вашего примера на этот код сможет снова скомпилироваться.

public class MyException12 extends Exception {
    public String getCustomValue(){ return "boo"; };
}

public class MyException1 extends MyException12{
    public String getCustomValue() { return "foo"; };
}

public class MyException2 extends MyException12{
    // constructors, etc
    public String getCustomValue() { return "foo"; };
}

Как и в вашем примере с интерфейсом, исключения уведомляют, что и MyException1, и MyException2 имеют MyException12 и, следовательно, могут использовать его функции.

Здесь - это ТАК вопрос, отвечающий на всю проблему и на то, каким будет тип e.

цитата из ссылки в ответе:

Изменение обработки типов исключений влияет на систему типов двумя способами: в дополнение к обычной проверке типов, выполняемой для всех типов, типы исключений подвергаются дополнительному анализу во время компиляции. В целях проверки типов параметр catch, объявленный с дизъюнкцией, имеет тип lub(t1, t2, ...) (JLSv3 §15.12.2.7), где ti — это типы исключений, для обработки которых объявлено предложение catch. Неформально lub (наименьшая верхняя граница) является наиболее конкретным супертипом рассматриваемых типов. В случае параметра исключения множественного перехвата всегда существует наименьшая верхняя граница рассматриваемых типов, поскольку типы всех перехваченных исключений должны быть подклассами Throwable. Следовательно, Throwable является верхней границей рассматриваемых типов, но может не быть наименьшей верхней границей, поскольку некоторый подкласс Throwable может быть суперклассом (и, следовательно, также супертипом) рассматриваемых типов, а рассматриваемые типы исключений могут реализовать общий интерфейс. (Lub может быть типом пересечения суперкласса и одного или нескольких интерфейсов.) В целях проверки исключений (JLSv3 §11.2) используется оператор throw (JLSv3 §11.2.2), который повторно выдает окончательный или фактически окончательный параметр catch. рассматривается как выбрасывание именно тех типов исключений, которые:

person SomeJavaGuy    schedule 27.08.2015
comment
Если принятый ответ по ссылке, размещенной в вашем ответе, верен, то интерфейс, который я использовал, никогда не должен разрешаться в e, потому что суперкласс обоих этих исключений не реализует его. - person Dariusz; 27.08.2015
comment
@Dariusz я отредактировал и процитировал ссылку в ответе на другой вопрос, уведомить о более поздней части, говоря: A lub can be an intersection type of a superclass and one or more interfaces. - person SomeJavaGuy; 27.08.2015
comment
Жаль, что эта формулировка не вошла в настоящую документацию по языку Java (которая написана очень неясно). Я думаю, наиболее близким является описание Intersection Types, в котором упоминается вывод типа (§15.12.2.7), а также описание клавиша catch. - person Andreas; 27.08.2015

В первом примере кода Java не может автоматически вывести, что и MyException1, и MyException2 реализуют функцию getCustomValue. Таким образом, e является наибольшим общим знаменателем в иерархии типов обоих типов; это Exception и не имеет функции getCustomValue. Таким образом, он не работает.

Java строго типизирована, и, несмотря на то, что функции названы одинаково, это не то же самое, как если бы они были объявлены в одном и том же типе.

Во втором коде оба исключения реализуют CustomValueGetter. Таким образом, eявляется наибольшим общим знаменателем CustomValueGetter, реализующим getCustomValue.

person lschuetze    schedule 27.08.2015

Копаясь в ссылке JLS @Kevin Eshche, я, кажется, нашел правильный ответ.

Во время компиляции e на самом деле является наименьшей верхней границей, которая определена в JLS-15.12.2.7.

Выдержка из документации:

Вычисление пересечения сложнее, чем может показаться на первый взгляд. Учитывая, что параметр типа должен быть надтипом двух различных вызовов универсального типа, скажем, List и List, наивная операция пересечения может дать Object. Однако более сложный анализ дает множество, содержащее List. Точно так же, если параметр типа T ограничен супертипом двух несвязанных интерфейсов I и J, мы можем сделать вывод, что T должен быть Object, или мы можем получить более жесткую границу I и J. Эти вопросы более подробно обсуждаются позже в эта секция.

Короче говоря, это class ближайшего общего супертипа всех | исключений (в моем случае Exception) и он реализует все интерфейсы, которые делают все исключения (в моем случае CustomValueGetter и Serializable).

person Dariusz    schedule 27.08.2015