1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-thinking-in-java-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
9.5 异常的限制.md 14 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 11.03.2025 09:15 d56454c

Ограничение на выбрасывание异常

При переопределении метода можно выбрасывать только те исключения, которые указаны в сигнатуре метода базового класса данного метода, или исключения, являющиеся потомками указанных исключений базового класса.

Пример ниже демонстрирует тип ограничений, накладываемых на исключения (в момент компиляции):

//: StormyInning.java
// Переопределенные методы могут выбрасывать только те
// исключения, которые указаны в сигнатурах методов базового класса,
// или исключения, являющиеся потомками указанных исключений базового класса.

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}

abstract class Inning {
  Inning() throws BaseballException {}
  void event () throws BaseballException {
    // В действительности ничего выбрасывать не обязательно
  }
  abstract void atBat() throws Strike, Foul;
  void walk() {} // Ничего не выбрасывает
}

class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
``````java
public class StormyInning extends Inning implements Storm {
   // Допускается добавление новых исключений для конструкторов,
   // но вы должны обрабатывать исключения базовых конструкторов:
   public StormyInning() throws RainedOut, BaseballException {}
   public StormyInning(String s) throws Foul, BaseballException {}

   // Обычные методы должны соответствовать базовому классу:
   //! void woodJoel() throws PopFoul {}; // Ошибка компиляции

   // К интерфейсу нельзя добавлять исключения к существующим методам базового класса:
   //! public void woodJent() throws RainedOut {};
   
   // Если метод еще не существует в базовом классе, исключение допустимо:
   public void woodRainHard() throws RainedOut {}

   // Вы можете выбрать не выбрасывать никаких исключений,
   // даже если версия базового класса это делает:
   public void woodJent() {}

   // Переопределенные методы могут выбрасывать наследуемые исключения:
   void woodAtBat() throws PopFoul {}

   public static void main(String[] args) {
      try {
         StormyInning si = new StormyInning();
         si.woodAtBat();
      } catch (PopFoul e) {
      } catch (RainedOut e) {
      } catch (BaseballException e) {}

      // Что происходит, если вы преобразуете тип?
      try {
         Inning i = new StormyInning();
         i.woodAtBat();

         // Вы должны поймать исключения от версии метода базового класса:
      } catch (Strike e) {
      } catch (Foul e) {
      } catch (RainedOut e) {
      }
   }
}
публичный класс StormyInning расширяет Inning
реализует Storm {
   // Допускается добавление новых исключений для конструкторов,
   // но вы должны обрабатывать исключения базовых конструкторов:
   StormyInning() бросает RainedOut, BaseballException {}
   StormyInning(строка s) бросает Foul, BaseballException {}

   // Обычные методы должны соответствовать базовому классу:
   //! void woodJoel() throws PopFoul {}; // Ошибка компиляции

   // К интерфейсу нельзя добавлять исключения к существующим методам базового класса:
   //! public void woodJent() throws RainedOut {};

   // Если метод еще не существует в базовом классе, исключение допустимо:
   публичный void woodRainHard() бросает RainedOut {}

   // Вы можете выбрать не выбрасывать никаких исключений,
   // даже если версия базового класса это делает:
   публичный void woodJent() {}

   // Переопределённые методы могут выбрасывать наследуемые исключения:
   void woodAtBat() бросает PopFoul {}

   публичный статический void основной(строка[] аргументы) {
      попробуй {
         StormyInning si = новый StormyInning();
         si.woodAtBat();
      } поймать(PopFoul e) {
      } поймать(RainedOut e) {
      } поймать(BaseballException e) {}

      // Что происходит, если вы преобразуете тип?
      попробуй {
         Inning i = новый StormyInning();
         i.woodAtBat();

         // Вы должны поймать исключения от версии метода базового класса:
      } поймать(Strike e) {
      } поймать(Foul e) {
      } поймать(RainedOut e) {
      }
   }
}
``````markdown
## 
```java
public class Inning {
    public Inning() throws BaseballException {}

    public void event() throws BaseballException {}
}

/// В методе `Inning` можно заметить, что как конструктор, так и метод `event()` указывают на то, что они могут "бросить" исключение, однако фактически этого не происходит. Это допустимо, так как это заставляет пользователя обязательно поймать любое исключение, которое может быть добавлено в переопределенной версии метода `event()`. То же самое относится к абстрактным методам, как показано в методе `atBat()`. Интерфейс interface Storm представляет интерес, так как он содержит метод event(), определённый в Incoming, а также другой метод, который не определён там. Оба этих метода могут "вызвать" новый тип исключения — RainedOut. Когда мы смотрим на StormyInning extends и implements Storm, становится очевидно, что метод event() из интерфейса Storm не может изменять интерфейс исключений метода event() из класса Inning. Такое проектирование является логичным; в противном случае, при работе с базовым классом, невозможно было бы узнать, правильно ли вы поймали исключение. Конечно, если метод, определённый в интерфейсе, отсутствует в базовом классе, например, метод rainHard(), это не вызывает проблем.

Ограничение на исключения не применяется к конструкторам. В классе StormyInning можно заметить, что конструктор может "вызвать" любое исключение, независимо от того, что вызывает конструктор базового класса. Однако, поскольку требуется обязательное использование конструктора базового класса (в данном случае автоматически вызывается конструктор по умолчанию), производный конструктор обязан объявить все исключения конструктора базового класса в своих спецификациях исключений.Метод StormyInning.walk() не компилируется, поскольку он "вызывает" исключение, в то время как метод Inning.walk() этого не делает. Если бы такое поведение было разрешено, можно было бы вызывать код, который использует Inning.walk(), без необходимости управления исключениями. Однако при замене объекта одного из классов, наследуемых от Inning, исключение будет "вызвано", что приведёт к прерыванию выполнения программы. Принуждая производные методы следовать спецификациям исключений базовых методов, можно обеспечить последовательность при замене объектов. Перекрытый метод event() показывает, что производный вариант метода может не генерировать никаких исключений — даже если базовый вариант должен это делать. Аналогично, это необходимо, так как это не прерывает код, который уже предполагает, что версия базового класса будет генерировать исключение. То же самое справедливо и для метода atBat(), который "вызывает" исключение PopFoul — производное от исключения Foul. Исключение Foul генерируется версией метода atBat() в базовом классе. Таким образом, если кто-то работает с объектами типа Inning и вызывает метод atBat(), ему придётся поймать исключение Foul. Поскольку PopFoul является производным от Foul, контроллер исключений также поймает исключение PopFoul. Последнее интересное место внутри main().В этом месте, если мы явно работаем с объектом типа StormyInning, компилятор заставляет нас ловить только те исключения, которые специфичны для этого конкретного класса. Однако, если мы преобразуем его в базовый тип, компилятор будет требовать от нас ловлю исключений, относящихся к этому базовому классу. Благодаря всем этим ограничениям, "жёсткость" контрольной точки исключений значительно улучшается (примечание ③).③: ANSI/ISO C++ также накладывает аналогичные ограничения, требуя, чтобы методы-потомки выбрасывали такие же исключения, как и методы базового класса, или производные от последних. В этом случае C++ действительно может проверять эти нормы исключений во время компиляции.

Мы должны признать это: хотя нормы исключений являются обязательными для соблюдения компилятором во время наследования, они не являются частью типа метода, который состоит лишь из имени метода и типов аргументов. Поэтому мы не можем переопределять методы на основе этих норм исключений. Кроме того, хотя нормы исключений могут существовать в версии метода базового класса, это не обязательно означает, что они должны существовать в версии метода производного класса. Это отличается от обычного наследования методов (где методы базового класса должны присутствовать в производном классе). Другими словами, "интерфейс норм исключений" для конкретного метода может становиться более "узким" при наследовании и переопределении, но он никогда не станет более "широким" — это прямо противоположно правилам наследования классовых интерфейсов.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-thinking-in-java-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-thinking-in-java-zh.git
oschina-mirror
wizardforcel-thinking-in-java-zh
wizardforcel-thinking-in-java-zh
master