Добрый день, уважаемые.
Вот мы тут с коллегой спорим как лучше сделать.
Метод init, системный, вызывается системой (при инициализации системы), бросает SystemException — это изменить нельзя.
Этот метод вызывает мой метод methodA.
Внутри methodA могут возникать два типа Exception: Ex1 и Ex2.
В случае любого из этих Exception init должен бросить SystemException.
Вопрос в том где обработать Ex1 и Ex2 и бросить SystemException: в методе methodA или в методе init (т.е. "внизу" или "вверху").
Здравствуйте, AlexisVo, Вы писали:
AV>Оба варианта будут работать одинаково правильно. Вопрос в том, как лучше, красивее.. где лучше стиль.
Особенную разницу в вариантах не углядел (если речь про методы одного и того же класса, то вообще какие-то гламурные размышления), а вот то, что SystemException затирает информацию о том, какое было получено исключение до этого: Ex1 или Ex2 — вижу. Протоколирование — по боку, главное, что клиентский код по отношению к методу init получает меньше информации о причине исключения. Собственно реализации умного слова в названии темы — делегирования — и не видно.
В JDK обычно делается так: исключение передается в конструктор исключения более высокого уровня иерархии, таким образом формируется последовательность причин, например:
, которую потом можно получить методом getStackTrace (привычный стек ошибки) или рекурсивно раскрутить через метод getCause — что, к примеру, делается для вот такой сигнализации с выводом отладочной информации: [офф] Диалог с детальным отображением стека ошибки
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, AlexisVo, Вы писали:
AV>>Оба варианта будут работать одинаково правильно. Вопрос в том, как лучше, красивее.. где лучше стиль. R>Особенную разницу в вариантах не углядел (если речь про методы одного и того же класса, то вообще какие-то гламурные размышления), а вот то, что SystemException затирает информацию о том, какое было получено исключение до этого: Ex1 или Ex2 — вижу. Протоколирование — по боку, главное, что клиентский код по отношению к методу init получает меньше информации о причине исключения.
Клиентскому коду эта инфа не нужна. Ее надо просто залогировать.
Вопрос в дизайне метода methodA.
Один из спорщиков (не будем переходить на личности, я это или не я) утверждает что раз в методе могут возникать несколько исключений, то они все должны быть перечислены в объявлении этого метода (в списке throws) не зависимо от того будут ли эти исключения обработаны по разному в методе верхнего уровня, или одинаково.. или вообще не обработаны...
Другой спорщик утверждает, что раз в методе верхнего уровня (init()), не нужна информация о том какое конкретно исключение произошло в methodA (Ex1 или Ex2), — а это так и есть, — то метод нижнего уровня и не должен наверх передавать лишнюю информацию.
Здравствуйте, AlexisVo, Вы писали:
AV>Клиентскому коду эта инфа не нужна. Ее надо просто залогировать.
Сейчас кажется, что не нужна, а завтра это станет нужно — зачем заранее делать криво?
AV>Вопрос в дизайне метода methodA. AV>Один из спорщиков (не будем переходить на личности, я это или не я) утверждает что раз в методе могут возникать несколько исключений, то они все должны быть перечислены в объявлении этого метода (в списке throws) не зависимо от того будут ли эти исключения обработаны по разному в методе верхнего уровня, или одинаково.. или вообще не обработаны... AV>Другой спорщик утверждает, что раз в методе верхнего уровня (init()), не нужна информация о том какое конкретно исключение произошло в methodA (Ex1 или Ex2), — а это так и есть, — то метод нижнего уровня и не должен наверх передавать лишнюю информацию.
Здесь все это есть: Теория и практика Java: Дебаты об исключениях. Отмечать или не отмечать?
Здравствуйте, AlexisVo, Вы писали: R>>Особенную разницу в вариантах не углядел (если речь про методы одного и того же класса, то вообще какие-то гламурные размышления), а вот то, что SystemException затирает информацию о том, какое было получено исключение до этого: Ex1 или Ex2 — вижу. Протоколирование — по боку, главное, что клиентский код по отношению к методу init получает меньше информации о причине исключения. AV>Клиентскому коду эта инфа не нужна. Ее надо просто залогировать.
Если задал вопрос — имей мужество воспринять ответ.
Сведение всех исключений к SystemException — ообязанность метода init, поскольку именно он брал на себя обязательство выбрасывать только его.
С другой стороны, именно он вызывает метод MethodA, и надо заранее договориться о его сигнатуре. Чтобы потом не было мучительно больно. Если MethodA — приватный и расположен в том же классе, что и init, т.е. всегда есть 100% шанс исправлять их код совместно, то можно оба исключения внести в его сигнатуру, и расширять ее по мере необходимости. Если метод — внешний по отношению к классу с методом init(), то лучше выбрать 1 (один) базовый класс для исключений, которые будет выбрасывать MethodA и в сигнатуру внести именно его, а Ex1 и Ex2 отнаследовать от этого класса. Это позволит в случае необходимости отнаследовать и Ex3, без внесения изменений в метод init().
В любом случае, исключение, выброшенное в вызываемом методе, должно быть вложено в новое исключение — обертку (SystemException в вашем случае). Пренебрежение этим очень вероятно аукнется в будущем. Имею на эту тему богатый негативный опыт.
AV>Вопрос в дизайне метода methodA. AV>Один из спорщиков (не будем переходить на личности, я это или не я) утверждает что раз в методе могут возникать несколько исключений, то они все должны быть перечислены в объявлении этого метода (в списке throws) не зависимо от того будут ли эти исключения обработаны по разному в методе верхнего уровня, или одинаково.. или вообще не обработаны...
AV>Другой спорщик утверждает, что раз в методе верхнего уровня (init()), не нужна информация о том какое конкретно исключение произошло в methodA (Ex1 или Ex2), — а это так и есть, — то метод нижнего уровня и не должен наверх передавать лишнюю информацию.
Намекните такому спорщику, что нужность информации существенно меняется со временем. Такие вещи, как
— непередача исходного исключения вверх по стеку
— непередача кода ошибки, на основе которого выброшено исключения
— непередача в исключении значения, которое вызвало out Of Range или точных причин InvalidOperation
— и все прочие сокрытия информации в процессе ее передачи между уровнями
можно приравнивать к правонарушениям, квалифицирующимся по статье 293 УК РФ.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, AlexisVo, Вы писали:
AV>Добрый день, уважаемые. AV>Вот мы тут с коллегой спорим как лучше сделать. AV>Метод init, системный, вызывается системой (при инициализации системы), бросает SystemException — это изменить нельзя. AV>Этот метод вызывает мой метод methodA.
AV>Внутри methodA могут возникать два типа Exception: Ex1 и Ex2. AV>В случае любого из этих Exception init должен бросить SystemException. AV>Вопрос в том где обработать Ex1 и Ex2 и бросить SystemException: в методе methodA или в методе init (т.е. "внизу" или "вверху").
Вот так
Вариант 3:
[java]
void init() throws SystemException {
try {
methodA();
} catch(Exception e) { // Throwable если надо ещё и Error-ы ловить
log.error(e);
throw new SystemException(e); // запомнить, что на самом деле это ошибка 'e'
}
}
AV>Оба варианта будут работать одинаково правильно. Вопрос в том, как лучше, красивее.. где лучше стиль.
Лучше стиль, когда ты любые исключения ловишь, а не только те, которые ты думаешь, что они будут кинуты.
И уж если ты в init задекларировал бросание SystemException, которое, очевидно, обёртка для других исключений — то в init их и надо ловить. Ведь это ошибка инициализации, а не ошибка methodA(). Ловить исключения надо там, где ты их осмысленно можешь обработать. В данном случае осмысленно — обернуть в системную ошибку и бросить дальше.
Здравствуйте, mkizub, Вы писали:
M> } catch(Exception e) { // Throwable если надо ещё и Error-ы ловить M>Лучше стиль, когда ты любые исключения ловишь, а не только те, которые ты думаешь, что они будут кинуты.
Ужас! Ни в коем разе так делать нельзя: Re[7]: [.NET, С#] Exception classes hierachy
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, mkizub, Вы писали:
M>> } catch(Exception e) { // Throwable если надо ещё и Error-ы ловить M>>Лучше стиль, когда ты любые исключения ловишь, а не только те, которые ты думаешь, что они будут кинуты. R>Ужас! Ни в коем разе так делать нельзя: Re[7]: [.NET, С#] Exception classes hierachy
Именно так и надо делать в данном случае. Это SystemException, то есть, очевидно, невозможность инициализировать программу. Это её не имеет смысла оборачивать во что-то, а она сама и есть правильная обёртка для всевозможных других исключений. Потому как об ошибке инициализации, программе надо просто сообщить и спокойно умереть. А ставить там проверку на все исключения от NullPointerException до ExceptionInInitializerError — глупо, ничего ни конечному пользователю не скажут. А программисту скажут, для этого 'e' и надо сохранять.
Ужас в том, что ты подходишь со своим стандартным решением, но в область где это решение неверно. То есть ты действуешь шаблонно, не думая, не понимая сути задачи. Вот в чём ужас.
ЗЫ А вот качество перевода на который ты ссылаешься — это вообще УЖАС-УЖАС-УЖАС.
Здравствуйте, mkizub, Вы писали:
M>Именно так и надо делать в данном случае. Это SystemException, то есть, очевидно, невозможность инициализировать программу. Это её не имеет смысла оборачивать во что-то, а она сама и есть правильная обёртка для всевозможных других исключений. Потому как об ошибке инициализации, программе надо просто сообщить и спокойно умереть. В нашем примере исключение SystemException явно является контролируемым (checked), так как декларируется в сигнатуре метода init. Поэтому оно не должно использоваться для индикации "программных ошибок":
Item 40: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors. Here, Bloch echoes the conventional Sun wisdom -- that runtime exceptions should be used only to indicate programming errors, such as precondition violations.
Item 41: Avoid unnecessary use of checked exceptions. In other words, don't use checked exceptions for conditions from which the caller could not possibly recover, or for which the only foreseeable response would be for the program to exit.
То есть если исключение является программной ошибкой, оно должно быть неконтролируемым, а если SystemException является индикатором каких-то программных ошибок, то и оно само также должно быть неконтролируемым. Собственно, просто непонятно, зачем программную ошибку нужно во что-то обертывать — выхлопа никакого от этого нет: полный stack trace ошибки и все, зачем что-то накручивать сверху?
Смысла в SystemException как в неконтролируемом (unchecked) исключении вообще нет:
In other words, Sun is telling us that checked exceptions should be the norm. The tutorial goes on to say, in several different ways, that you should generally throw Exception and not throw RuntimeException -- unless you're the JVM.
Иными словами, практически нет никакого смысла делать свое (не из JDK) неконтролируемое исключение, которое было бы индикатором других неконтролируемых исключений, проще дать зеленый свет исходному неконтролируемому исключению — информативность будет та же.
M>А ставить там проверку на все исключения от NullPointerException до ExceptionInInitializerError — глупо, ничего ни конечному пользователю не скажут. А программисту скажут, для этого 'e' и надо сохранять.
Хм... Вообще говоря, это именно вы выше предложили хватать все-все исключения (могу еще принять, что иногда java.lang.RuntimeException стоит перехватывать, но не сбои же виртуальной машины java.lang.Error!), а потом еще и упаковывать все это хозяйство в контролируемое исключение.
Зачем это?
package ru.rsdn;
public class Test {
public static void main(String... args) {
try {
throw new RuntimeException();
//throw new Error();
} catch (Throwable e) {
try {
throw new Exception(e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
Если оно эквивалентно этому:
package ru.rsdn;
public class Test {
public static void main(String... args) {
throw new RuntimeException();
//throw new Error();
}
}
Но при этом дает более запутанный stack trace.
M>Ужас в том, что ты подходишь со своим стандартным решением,
Это не мое, это рекомендации Sun Microsystems.
M>но в область где это решение неверно.
Что за область такая?
M>То есть ты действуешь шаблонно, не думая, не понимая сути задачи. Вот в чём ужас.
А суть задачи в чем?
M>ЗЫ А вот качество перевода на который ты ссылаешься — это вообще УЖАС-УЖАС-УЖАС.
Очень смешная попытка кусануть.
Специально для вас цитаты из оригинала.
Здравствуйте, rsn81, Вы писали:
R>[list=1]В нашем примере исключение SystemException явно является контролируемым (checked), так как декларируется в сигнатуре метода init. Поэтому оно не должно использоваться для индикации "программных ошибок"
его имеет смысл делать checked. Если же init() может вызываться в нескольких местах во время выполнения программы — его имеет смысл делать unchecked. В этом случае оно будет аналогом ExceptionInInitializer, которое unchecked, а декларация в init() нужна только как средство документации.
И в том и в другом случае оно по названию является фатальной ошибкой, в отличие от простого RuntimeException, которые возникают где попало, но чаще всего не фатальны для приложения (если их корректно ловить). Именно поэтому все ошибки, произошедшие во время инициализации, имеет смысл обернуть в этот SystemException. Точно так-же, как в ExceptionInIntializer оборачиваются все ошибки произошедшие при инициализации.
M>>А ставить там проверку на все исключения от NullPointerException до ExceptionInInitializerError — глупо, ничего ни конечному пользователю не скажут. А программисту скажут, для этого 'e' и надо сохранять. R>Хм... Вообще говоря, это именно вы выше предложили хватать все-все исключения (могу еще принять, что иногда java.lang.RuntimeException стоит перехватывать, но не сбои же виртуальной машины java.lang.Error!), а потом еще и упаковывать все это хозяйство в контролируемое исключение.
Наборот, в моём коде Error не ловился, я только в комментарии сказал, что если хочется и Error ловить, то тогда надо делать catch(Throwable).
Checked или unchecked это исключение зависит только от дизайна программы, не надо на меня навешивать свои предположения.
M>>Ужас в том, что ты подходишь со своим стандартным решением, R>Это не мое, это рекомендации Sun Microsystems.
Для других ситуаций. Как это сделано в Sun — смотри ExceptionInIntializer и подобных.
M>>но в область где это решение неверно. R>Что за область такая?
Фатальная ошибка при инициализации приложения.
M>>То есть ты действуешь шаблонно, не думая, не понимая сути задачи. Вот в чём ужас. R>А суть задачи в чем?
Вот и начал бы с прочтения начального поста.
M>>ЗЫ А вот качество перевода на который ты ссылаешься — это вообще УЖАС-УЖАС-УЖАС. R>Очень смешная попытка кусануть. R>Специально для вас цитаты из оригинала.
Спасибо. Осталось только цитаты под данный случай найти, а не первые попавшиеся.
M>Это зависит от дизайна. В случае
[skipped] M>его имеет смысл делать checked. Если же init() может вызываться в нескольких местах во время выполнения программы — его имеет смысл делать unchecked. В этом случае оно будет аналогом ExceptionInInitializer, которое unchecked, а декларация в init() нужна только как средство документации. M>И в том и в другом случае оно по названию является фатальной ошибкой, в отличие от простого RuntimeException, которые возникают где попало, но чаще всего не фатальны для приложения (если их корректно ловить). Именно поэтому все ошибки, произошедшие во время инициализации, имеет смысл обернуть в этот SystemException. Точно так-же, как в ExceptionInIntializer оборачиваются все ошибки произошедшие при инициализации. java.lang.ExceptionInInitializerError unchecked-исключение (более того, наследник java.lang.Error), а потому к данному случаю вовсе не относится.
checked-исключения вовсе не являются именно "средством документации", слабый аргумент в сторону того, чтобы делать исключение отмеченным.
ExceptionInInitializer имеет специальную семантику в рамках JVM: сбой статической инициализации класса к разговору имеет какое-то очень отдаленное отношение:
Signals that an unexpected exception has occurred in a static initializer. An ExceptionInInitializerError is thrown to indicate that an exception occurred during evaluation of a static initializer or the initializer for a static variable.
В ExceptionInInitializer запихиваются вовсе не все возможные исключения, к примеру, нехватка памяти будет так и брошена в виде java.lang.OutOfMemoryException:
If the Java Virtual Machine attempts to create a new instance of the class ExceptionInInitializerError but is unable to do so because an Out-Of-Memory-Error occurs, then the OutOfMemoryError object is thrown instead.
M>Наборот, в моём коде Error не ловился, я только в комментарии сказал, что если хочется и Error ловить, то тогда надо делать catch(Throwable).
Хорош совет, ничего не скажешь.
M>Checked или unchecked это исключение зависит только от дизайна программы, не надо на меня навешивать свои предположения. M>Для других ситуаций. Как это сделано в Sun — смотри ExceptionInIntializer и подобных.
Javadoc по ExceptionInIntializer давно смотрели?
M>Фатальная ошибка при инициализации приложения.
От вашей обертки никакого выхлопа, начните с этого и не приплетайте ExceptionInIntializer.
M>Вот и начал бы с прочтения начального поста. M>Спасибо. Осталось только цитаты под данный случай найти, а не первые попавшиеся.
На здоровье.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Throws specification из Джавы есть уродство,
Да, ну?
MSS>и красивых методов обхода этого маразма не существует.
Зато некрасивых предостаточно
Здравствуйте, AlexisVo, Вы писали:
AV>Вариант 1: AV>Вариант 2: AV>Оба варианта будут работать одинаково правильно. Вопрос в том, как лучше, красивее.. где лучше стиль.
На абстрактном коде разбирать оба случая не имеет смысла. Надо смотреть контекст:
— В каких классах находятся методы. Находятся ли эти классы в разных слоях.
— За что эти классы отвечают.
— За что так же отвечают все три исключения.
Тогда и будет видно кто в каком случае клиент, а кто сервис и кто должен логировать и обрабатывать ошибки.
На абстрактном примере абсолютно все равно как делать.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Какое user-sensible value дает эта дурацкая фича? необходимость корявых обходов идиотским кодом, который перекладывает одни исключения в другие?
Не допускали мысль, что попросту не овладели рецептом приготовления этой фичи?
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Какое user-sensible value дает эта дурацкая фича? необходимость корявых обходов идиотским кодом, который перекладывает одни исключения в другие?
Checked исключения подталкивают девелопера к тому чтобы думать о системе исключений во всем приложении. Неконтролируемые исключения ни к чему не поддталкивают, тем самым способствуют попустительству в плане обработки исключений.
B>Checked исключения подталкивают девелопера к тому чтобы думать о системе исключений во всем приложении. Неконтролируемые исключения ни к чему не поддталкивают, тем самым способствуют попустительству в плане обработки исключений.
В 99% случаев все исключения передаются без потери информации до какого-то высокого уровня (иногда просто до юзера), где пишутся в лог или же показываются юзеру.
Необходимость в exception contracts возникает дай бог в 1% случаев, когда, например, некоторым ошибкам делается retry, некоторые ошибки есть просто ответ "нет" на корректно заданный вопрос (типа "нет такого файла") и так далее. Такого дай бог 1%.
А значит, в 99% случаев просто не важно, как именно обломилась функция. В коде не важно — при деплойменте может быть и важно.
Значит, в 99% случаев throws spec не дает никаких благ.