Подтвердить что ты не робот

Определение типа исключения множественной выборки во время компиляции

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

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

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

4b9b3361

Ответ 1

Как сказал Ишчутзе, 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"; };
}

Как и в вашем примере с интерфейсом, Exceptions уведомляет, что оба 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, рассматриваются как бросающие именно те типы исключений, которые:

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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