#java #inner-classes #nested-class
#java #внутренние классы #вложенный класс
Вопрос:
Каков наилучший практический способ получения прозрачности исключений в Java при использовании анонимного внутреннего класса для запуска некоторого кода.
Частым шаблоном, который я видел в реальном коде, является использование некоторого интерфейса псевдообрабатываемого типа для указания некоторого контекста для некоторого заданного кода. Лучший пример, который я могу придумать в JDK, — это java.security.PrivilegedExceptionAction.
try {
boolean success = AccessController.doPrivileged(
new PrivilegedExceptionAction<Boolean>() {
@Override
public Boolean run() throws Exception {
// do something
// read file
FileInputStream fileInputStream =
new FileInputStream(new File("someFile"));
return true;
}
}
);
} catch (PrivilegedActionException e) {
if (e.getCause() instanceof FileNotFoundException) {
// handle IO exception
} else {
// impossible no other checked exception
}
}
Хотя при чтении кода вы можете четко видеть, что внутренний код выдает только файл, который не найден, но мы потеряли преимущества проверенных исключений, поскольку вызывающий объект не знает, какое исключение на самом деле генерируется. Распространенной ошибкой было бы вводить код в анонимный внутренний класс, который вызывал бы новое исключение, и код не заставлял бы вас обрабатывать это исключение.
То, что я хочу, это что-то вроде приведенного ниже, достижим ли этот тип поведения без изменения языка?
public interface PrivilegedExceptionAction<T,V... extends Throwable>
{
public T run() throws V;
}
Комментарии:
1. Я думаю, что анонимные внутренние классы, в общем, плохая идея, главным образом потому, что их нельзя использовать повторно или расширять. Но это другая причина. Меня тоже интересует любой ответ.
2. Взгляните на blogs.sun.com/briangoetz/entry/exception_transparency_in_java
3. Я видел предложение Брайана в project lambda, к сожалению, это недоступно в java 6
4. sun.com ссылки теперь мертвы. Упомянутая статья теперь находится здесь blogs.oracle.com/briangoetz/entry /…
Ответ №1:
Я не понимаю, почему нет. Следующий код сработал.
interface RunIt < E extends Exception >
{
void run ( ) throws E ;
}
class App
{
public static void main ( String [ ] args )
{
RunIt < RuntimeException > r = new RunIt < RuntimeException > ( )
{
public void run ( )
{
throw new RuntimeException ( ) ;
}
} ;
r . run ( ) ;
}
}
Комментарии:
1. Что бы вы сделали, если вам нужно создать более одного исключения? Был бы у вас RunIt2<E1 расширяет исключение,E2 расширяет исключение>. Я думаю, что это не так уж плохо. Мне пришлось бы выбирать, какой интерфейс использовать, в зависимости от того, какие проверенные исключения я ожидаю выбросить.
2. Если вам нужно создать более одного исключения, есть 2 варианта. (1) как вы говорите, E1 расширяет исключение, E2 расширяет исключение. (2) Вы могли бы выбрать E в качестве суперкласса из всех исключений, которые могут быть сгенерированы. Если вы выберете 1, то интерфейс может выглядеть как RunIt<E1 расширяет исключение, E2 расширяет исключение, … , E100 расширяет исключение >, чтобы разрешить множество видов проверяемых исключений. (Если их больше, чем вам нужно, вы можете установить превышение в RuntimeException .) Если вы выберете 2, такой тип не соответствует цели упражнения. Это не самое лучшее решение, но оно работает.