Хорошая модель? <X extends Exception>... method() выбрасывает X
Некоторые предпосылки, затем некоторые вопросы.
Я только недавно обнаружил, что интерфейс (или класс) может быть общим в типе (проверенном) исключении, которое могут вызвать его методы. Например:
interface GenericRunnable<X extends Exception> {
void run() throws X;
}
Дело в том, что если позже вы создадите экземпляр с помощью, скажем, IOException
и вызовите метод run
, компилятор знает, что вам нужно либо поймать IOException
, либо пометить его как брошенное. Еще лучше, если X
был RuntimeException
, вам вообще не нужно его обрабатывать.
Здесь надуманный пример с использованием вышеприведенного интерфейса, но он в основном обратный вызов и должен быть довольно распространенным.
public <X extends Exception> void runTwice(GenericRunnable<X> runnable) throws X {
runnable.run(); runnable.run();
}
...
public myMethod() throws MyException {
runTwice(myRunnable);
}
Мы вызываем общий метод утилиты runTwice
(возможно, определенный во внешней библиотеке) для запуска нашего собственного метода с определенным проверенным исключением, и мы не теряем информацию о том, какое конкретное исключенное исключение может быть выбрано.
Альтернативой было бы просто использовать throws Exception
как для метода Runnable.run
, так и для метода runTwice
. Это не будет ограничивать реализацию интерфейса Runnable
, но преимущество проверенных исключений будет потеряно. Или вообще не могло быть throws
, также теряя преимущество проверенных исключений и потенциально заставляя реализацию обернуть.
Потому что я никогда не видел throws X
, возможно, я что-то пропустил. Кроме того, я видел пример обратного вызова, который использовался как аргумент против отмеченных исключений несколько раз, без его опровержения. (Этот вопрос не интересует плюсы и минусы проверенных исключений.)
Является ли throws X
хорошей идеей? Каковы плюсы и минусы? Можете ли вы привести несколько примеров, которые либо используют throws X
, либо не должны, но должны иметь?
В принципе, я хотел бы получить дополнительную информацию. Вы можете прокомментировать следующие примеры.
-
OutputStream
throws IOException
(возможно, ByteArrayOutputStream
может расширять GenericOutputStream<RuntimeException>
)
-
Callable
/Future.get
-
Commons Pool borrowObject
/makeObject
(Начиная с редактирования, я не прошу, чтобы они могли/должны были быть спроектированы по-разному ретроспективно. Скорее, throws X
был бы лучше, чем throws Exception
.)
Ответы
Ответ 1
Я использую этот шаблон все время, в основном для java функционального стиля.
Плюсы:
-
У вас может быть несколько ароматов шаблона более высокого порядка, такого как Visitor, чтобы:
interface ExceptionalVoidVisitor< E extends Exception > {
void handleA( A a ) throws E;
void handleB( B b ) throws E;
}
interface VoidVisitor extends ExceptionalVoidVisitor< RuntimeException > {}
interface ExceptionalVisitor< T, E extends Exception > {
T handleA( A a ) throws E;
T handleB( B b ) throws E;
}
interface Visitor< T > extends ExceptionalVisitor< T, RuntimeException > {}
-
Клиент может объявить базовый класс исключений для всех исключений, которые он может бросить, и вы получите полностью общую библиотеку.
Минусы:
- Как вы обнаружили, нет способа обработать исключение родового типа; вы должны позволить ему сбежать.
- Другой вариант - принять генератор E, который может быть неудобным для клиентов.