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

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

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

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 экземпляр динамически сгенерированного класса, который реализует все общие интерфейсы обоих исключений и имеет тип ближайшего общего суперкласса?

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 и, следовательно, могут использовать его функции.

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

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

Изменение обработки типов исключений влияет на систему типов двумя способами: в дополнение к обычной проверке типов, выполняемой для всех типов, типы исключений подвергаются дополнительному анализу времени компиляции. В целях проверки типа параметр 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, является трактуется как выбрасывание именно тех типов исключений, которые:

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

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

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

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

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

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

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

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

Другие вопросы по тегам