107

107


Одним из важных достижений Java стала унификация модели передачи ин¬формации об ошибках, так как обо всех ошибках сообщается посредством ис¬ключений. В С++ этого не было, из-за обратной совместимости с С и возмож¬ности задействовать старую модель простого игнорирования ошибок. Когда Java изменил модель С++ так, что сообщать об ошибках стало возможно только посредством исключений, необходимость в дополнительных мерах принуждения в виде контролируемых исключений сократилась.

В прошлом я твердо считал, что для разработки надежных программ необхо¬димы и контролируемые исключения, и строгая статическая проверка типов. Однако опыт, полученный лично и со стороны1, с языками, более динамичны¬ми, чем статичными, привел меня к мысли, что на самом деле главные преиму¬щества обусловлены следующими аспектами:

1. Унификация модели сообщения об ошибках посредством исключений (независимо от того, заставляет ли компилятор программиста их обраба¬тывать).

2. Проверка типов, не привязанная к тому, когда она проводится — на ста¬дии компиляции или во время работы программы.

Вдобавок снижение ограничений времени компиляции весьма положитель¬но отражается на продуктивности программиста. С другой стороны, для ком¬пенсации чрезмерной жесткости статической проверки типов необходимы реф¬лексия и параметризация, как вы убедитесь в некоторых примерах книги.

Некоторые уверяли меня, что все сказанное является кощунством, безна¬дежно испортит мою репутацию, приведет к гибели цивилизации и провалу большой доли программных проектов. Вера в то, что выявление ошибок на ста¬дии компиляции спасет ваш проект, весьма сильна, но гораздо важнее созна¬вать ограничения того, на что способен компьютер. Стоит помнить:

«Хороший язык программирования помогает программистам писать хоро¬шие программы. Ни один из языков программирования не может запретить сво¬им пользователям писать плохие программы ».

В любом случае исчезновение когда-либо из Java контролируемых исключе¬ний весьма маловероятно. Это слишком радикальное изменение языка, и защит¬ники их в Sun весьма сильны. История Sun неотделима от политики абсолютной обратной совместимости — фактически любое программное обеспечение Sun ра¬ботает на любом оборудовании Sun, как бы старо оно ни было. Но, если вы чув¬ствуете, что контролируемые исключения становятся для вас препятствием (особенно если вас заставляют обрабатывать исключение, а вы не знаете, как с ним поступить), существует несколько вариантов.

Передача исключений на консоль

В несложных программах, как во многих примерах данной книги, простейшим решением является передача исключения за пределы метода main(), на консоль. К примеру, при открытии файла для чтения (подробности вы вскоре узнаете) необходимо открыть и закрыть поток FilelnputStream, который возбуждает ис¬ключения. В небольшой программе можно поступить следующим образом (по¬добный подход характерен для многих примеров книги):

//• excepti ons/Mai nExcepti on.java

import java io *;

public class MainException {

// Передаем все исключения на консоль, public static void main(String[] args) throws Exception { // Открываем файл: FilelnputStream file =

new FilelnputStreamC'MainException.java");

// Используем файл // Закрываем файл- file.closeO.

}

} ///:-

Заметьте, что main() — такой же метод, как и все прочие; он тоже может иметь спецификацию исключений, и здесь типом исключения является Excep¬tion, базовый класс всех контролируемых исключений. Передавая его на кон¬соль, вы освобождаетесь от необходимости написания предложений try-catch в теле метода main().

Преобразование контролируемых исключений в неконтролируемые

Рассмотренный выше подход хорош при написании метода main(), но в более общих ситуациях не слишком полезен. Подлинная проблема возникает при на¬писании тела самого обычного метода, когда при вызове другого метода вы чет¬ко сознаете: «Понятия не имею, что делать с исключением дальше, но „съедать" мне его не хочется, так же как и печатать банальное сообщение». Проблема ре¬шается при помощи цепочек исключений. Управляемое исключение просто «заворачивается» в класс RuntimeException примерно так:

try {

// .. делаем что-нибудь полезное } са!сИ(НеЗнаюЧтоДелатьСЭтимКонтролируемымИсключением е) { throw new RuntimeException(e);

}

Решение идеально подходит для тех случаев, когда вы хотите «подавить» контролируемое исключение: вы не «съедаете» его, вам не приходится описы¬вать его в своей спецификации исключений, и благодаря цепочке исключений вы не теряете информацию об исходном исключении.

Описанная методика позволяет игнорировать исключение и пустить его «всплывать» вверх по стеку вызова без необходимости писать блоки try-catch и (или) спецификации исключения. Впрочем, при этом вы все равно можете пе¬рехватить и обработать конкретное исключение, используя метод getCause(), как показано ниже:

// exceptions/TurnOffChecking.java // "Подавление" контролируемых исключений, import java io *;

import static net mindview.util.Print.*;

class WrapCheckedException {

void throwRuntimeException(int type) { try {

switch(type) {

case 0: throw new FileNotFoundExceptionO; case 1: throw new IOExceptionO; case 2- throw new RuntimeExceptionCTfle Я?"). default: return;

}

} catch(Exception e) {

// Превращаем в неконтролируемое: throw new RuntimeException(e);

}

}

}

class SomeOtherException extends Exception {}

public class TurnOffChecking {

public static void main(String[] args) {

WrapCheckedException wee = new WrapCheckedExceptionO: // Можно вызвать throwRuntimeExceptionO без блока try, // и позволить исключению RuntimeException покинуть метод- wee. throwRuntimeExceptionO); // Или перехватить исключение: for (int i =0; i <4; i++) try {

if(i < 3)

wee throwRuntimeException(i);

else

throw new SomeOtherExceptionO: } catch(SomeOtherException e) {

print("SomeOtherException. " + e); } catch(RuntimeException re) {

try { продолжение &

throw re.getCauseO, } catch(FileNotFoundException e) {

print("FileNotFoundException. " + e); } catch(IOException e) {

print("IOException- " + e); } catch(Throwable e) {

print("Throwable. " + e);

Report Page