- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
public static <T> T createInstance(String className, Object ... ctorParams)
{
Class<T> type;
try {
type = (Class <T>) Class.forName(className);
}
catch (ClassNotFoundException e) { throw new RuntimeException(e); }
Class <?> [] paramTypes = new Class [ctorParams.length];
for(int i = 0; i < ctorParams.length; i ++)
paramTypes[i] = (Class <?>) ctorParams[i].getClass();
Constructor<T> ctor;
try {
ctor = type.getConstructor(paramTypes);
}
catch (SecurityException e) { throw new RuntimeException(e); }
catch (NoSuchMethodException e){ throw new RuntimeException(e); }
T instance;
try {
instance = ctor.newInstance(ctorParams);
}
catch (IllegalArgumentException e) { throw new RuntimeException(e); }
catch (InstantiationException e) { throw new RuntimeException(e); }
catch (IllegalAccessException e) { throw new RuntimeException(e); }
catch (InvocationTargetException e) { throw new RuntimeException(e); }
return instance;
}
Тут само Java вынуждает говнокодить. О святая простота!
хотите простоты? попробуйте груви
патамушта слова "праграмист" рихмуется са словам "мазахист".
там такая функция встроенная, и вообще базовая библиотека классов более объёмная и на все случаи жизни.
груви генерирует такой же jvm-байткод, как и java
> там такая функция встроенная
о да, и без того прогеры плюются, что на уровне языка много чего странного понапихано
А Activator.CreateInstance -- действительно хорошая штука, я её использовал очень много раз.
наверное, и там и там можно отключить, но мне пофигу
:)
catch(B e1) {
throw e1;
}
catch(D e2) {
// do something
}
Class<T> type = (Class <T>) Class.forName(className);
Class <?> [] paramTypes = new Class [ctorParams.length];
for(int i = 0; i < ctorParams.length; i ++)
paramTypes[i] = (Class <?>) ctorParams[i].getClass();
Constructor<T> ctor = type.getConstructor(paramTypes);
return ctor.newInstance(ctorParams);
}
catch (Exception e) {
throw new RuntimeException(e);
}
Проблема скорее надумана, чем реальна.
С удовольствием взгляну на реальные примеры.
Просто пример — коммандный интерпретатор. Построчно читает ввод (здесь возможны ошибки в/в), парсит строку, по первой строке находит команду (возможна ошибка неверного имени команды), диспетчирезирует по нему и передаёт параметры вниз. В самом низу параметры парсятся специфичным способом и выбрасываются исключения (с необходимой контекстно-специфичной информацией) в случае неверных данных. Также могут возникнуть неожиданные исключения из-за ошибок программирования (подробности показывать пользователю нельзя).
public void doSomething() {
String input = readCommand();
Command command = parseCommand(input);
executeCommand(command);
}
private String readCommand() { ... // Обработать ошибки ввода/вывода }
private Command parseCommand(String input) { ... // Обработать ошибки ввода пользователя}
private void executeCommand(Command command) {... // обработывать ошибки в программировании}
На то чтобы понять, что делает метод doSomething() уйдут секудны. Если все будет в одном методе - можно и на час застрять.
Ошибки ввода пользователя обрабатываются на самом нижнем уровне, в совсем другом классе, уже вовремя исполнения, потому, что только там известно, какие данные допустимы для этой команды.
Что именно делать с разного типа ошибками — известно только на верхнем уровне, в точке перехвата. Вот пусть и будут разные перехватчики для разных типов.
Приведу пример. Например, readCommand() читает данные из файла. Возможные проблемы: файл не существует, файл не может быть прочитан или вообще какая-нить неведомая фигня. В терминах бизнес логики, любая из этих ошибок, означает одно - мы не можем получить входные данные. Для сообщения используем new UserInputException(e). Например, на верхнем уровне нас интересует ситуация когда файл не найден, а все остальные для нас равнозначны. Тогда добавляем в наш UserException булево поле fileNotFound. И на верхнем уровне анализируем именно его, а не getCause().
Также этот подход имеет еще один "+". К примеру, мы ожидам, что IllegalArgumentException может быть ТОЛЬКО внутри executeCommand. И если он там возникает мы оборачиваем его в CommandExecutionException и выкидываем наверх. Где его соответственно и ловим. Теперь, если на верхний уровень выпадет IllegalArgumentException это будет означать только одно - в приложении где-то баг, т. к. наши ожидания не оправдались.
Еще раз повторюсь: не стоит смешивать уровни абстракции.
Кроме того, юмор был на самом деле по поводу типичного для Java стиля написания кода, когда каждая функция выбрасывает кучу разнообразных ошибок очень сильно запутывая логику программы в целом. Но если речь уже зашла о неограниченном и разнородном уровне абстракций... мне стало страшно :)
А на счет "типичного для Java стиля написания кода, когда каждая функция выбрасывает кучу разнообразных ошибок..." - без комментариев =)
Т.е. предположим, что вам кажется нормальным код:
а такой:
:)
вы приделываете костыль, т.как ошибку достаточно бросить один раз, чтобы она дошла до самого верха, но изза того, что вы не можете отфильтровать только нужные, вам прийдется одну и ту же ошибку бросать много раз.
П. С. Вы случайно не сишник?
Вы в этом эпизоде писали:
Что для того, чтобы решить проблему инвариантых ошибок вы сначала обработаете более узкоспециализированные ошибки, которые вобщем-то обрабатывать не нужно, но вы их просто еще раз бросите, чтобы таким образом они не затесались среди более широкоспециализированной группы ошибок, которую вы обработаете далее, единообразно. Я же говорю, что налицо избыточность, т.как ошибка, по определению будет подыматься по стеку вызовов, пока не будет обработана. А вы ее, по-факту, не обрабатываете, а просто избегаете обработки - этот код избыточен, но избыточность продиктована реалиями языка. Язык не предоставляет инструмента более точно отфильтровать ошибки, которые нужно обрабатывать.
Более коротко, вот этого кода:
могло бы не быть, если бы вы могли более точно описать тип ошибки. Просто в сознании Java-программистов тип неотделим от класса или интерфейса, а родовая принадлежность воспринимается как шаблон, и типом не считаются. Для функций придуман механизм, который перекрывает необходимость в настоящих генериках процентов на 80, но в некоторых местах о нем просто "забыли", как, например, в catch.
В Java 7 это решили так:
catch(Exc1 | Exc2 | Exc3 e) {...}
when Exc1 | Exc2 | Exc3 => ...
when others => raise;
end;
Как раз вы смешиваете уровни абстракции (протягивая какие-то флаги). Есть, грубо говоря, два уровня. На верхнем в цикле происходит ввод строки, разбор, игнорирование комментариев и пустых строк, склейка строк с продолжением, выделение имени команды, поиск обработчика команды, вызов обработчика команды, вывод результатов и разнообразная обработка ошибок, возникающих в процессе ввода/вывода и исполнения команд. На нижнем — интерпретация и разбор параметров команды, выполнение команды.
В случае предусмотренной ошибки, возникающей на нижнем уровне, формируется UserException с соответствующим сообщением, сопровождается интересующей информацией (например, для ошибок в параметрах будет свой подкласс UserArgumentException и атрибут — номер и значение параметра) и пробрасывается наверх. На нижнем уровне мы можем определить пользовательскую ошибку, но мы не должны беспокоиться, как донести её до пользователя.
На верхнем уровне ловятся как ошибки ввода/вывода (часть можно обработать в подфункциях, не передавая наверх, часть влияет на главный цикл), так и UserException с подклассами, прилетевшие из обработки команд. И на каждое исключение реагирует по-своему — на UserArgumentException выводит строку с указанием ошибочного параметра, на общий UserException — сформированное сообщение об ошибке, на разрыв связи — заканчивает работу. Чем именно вызван UserException — на этом уровне неважно, есть сообщение и другие параметры, которые нужно отобразить (но способ отображения может зависеть от подтипа).
и очень кошерно словить сюда какой-нибудь OutOfMemoryError или StackOverflowError
П. С. Переходи на Java 7. Там с этим делом чутка получше
То что говнокод - это да. То что джава к этому побуждает - это НЕТ.
П. С. Советую посмотреть try/catch паттерны.
* InvocationTargetException - нужен скорее e.getCause().getMessage()
* InstantiationException выдает пустой getMessage()
* IllegalArgumentException в данном контексте невнятен,
Но вообще да, это все Eclipse :)
Я имел ввиду "throw new RuntimeException(e)". А если потребуется поменять RuntimeException еще на что-нибудь? Или логировать ошибку? В самом простом случае, можно было бы заменить "throw new RuntimeException(e)" на "logAndThrow(e)".
private static void logAndThrow(Exception e) {
logger.error("", e);
throw new RuntimeException(e);
}
По крайней мере в случае смены типа ошибки изменения надо будет вносить лишь в одной строчке, а не в семи, как в исходном примере.
Разве что надо было делать throw с указанием cause, чтобы исходную причину не потерять, и обернуть всё в один try/catch блок.
[КО]
шаблоны С++ компилируются в перегруженные методы
дженерики джавы теряются при компиляции и далее <T extends TT> понимается просто как ТТ
[/КО]
Никто никого не вынуждает, метод в идеале должен выкидывать из себя кастомный эксепшн приложения или (формально то же самое, архитектурно - неверно) возвращать нулл. Ну и блок try - catch тут нужен только один.