Хороший узор? ‹X расширяет исключение› метод() выдает 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 бросает IOException (возможно, ByteArrayOutputStream может протягивать GenericOutputStream<RuntimeException>)

  • Callable / Future.get

  • Общественный пул borrowObject / makeObject

(После редактирования я не спрашиваю, могли ли/должны ли они быть спроектированы по-другому в ретроспективе. Скорее, будет ли throws X лучше, чем throws Exception.)


person user2357    schedule 09.02.2014    source источник
comment
Потоковые классы не могли быть спроектированы таким образом, потому что они были разработаны до того, как эта функция была добавлена ​​(т. е. дженерики).   -  person tbodt    schedule 09.02.2014
comment
В Throwables в Guava есть некоторые Примеры. Но этот вопрос кажется слишком широким. Постарайтесь усовершенствовать свои различные вопросы в посте, сделав их более сфокусированными.   -  person Paul Bellora    schedule 09.02.2014
comment
Ага. Java развивалась с течением времени. Это означает, что в старых классах/функциях всегда будут места, где более новые могли использоваться, но были недоступны, а поскольку все работает достаточно хорошо и без них, для этого не было достаточных оснований. Конечно, вы можете реализовать свои собственные оболочки/переизобретения старых классов, чтобы использовать новые функции, и если они действительно окажутся достаточно полезными, их можно будет принять; на самом деле это часть того, как выросли библиотеки Java. Но это происходит только тогда, когда есть достаточная реальная добавленная стоимость и достаточный спрос, чтобы оправдать это.   -  person keshlam    schedule 09.02.2014
comment
@keshlam И никаких поломок при этом ..   -  person user2864740    schedule 09.02.2014
comment
Одним из недостатков дженериков является то, что вам нужно очень тщательно проектировать классы и API, чтобы вы могли сохранить тип. Поместите более 1 дженерика «X» в список с другими дженериками «Y» — и все готово. Я думаю, что во многих случаях это означало бы, что вы не могли бы фактически использовать этот тип, но должны были бы иметь дело с Generic‹?›.   -  person zapl    schedule 10.02.2014


Ответы (1)


Я использую этот шаблон все время, в основном для функционального стиля Java.

Плюсы:

  1. У вас может быть несколько разновидностей шаблона более высокого порядка, такого как Посетитель, а именно:

    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 > {}
    
  2. Ваш клиент может объявить базовый класс исключений для всех исключений, которые он может генерировать, и вы получите полностью общую библиотеку.

Минусы:

  1. Как вы уже поняли, нет способа обработать исключение универсального типа; вы должны позволить ему убежать.
  2. Другой вариант — принять генератор E, что может быть неудобно для клиентов.
person Judge Mental    schedule 09.02.2014
comment
Приятно слышать, что это обычная картина для вас. Я не буду беспокоиться о том, чтобы использовать его для себя в будущем. - person user2357; 18.02.2014