Можно ли в Java объединить идентичную логику обработки исключений в одном месте?

Я пытаюсь очистить код Java. Существует множество статических фабричных методов, которые выполняют одинаковую обработку исключений. В качестве примера рассмотрим createA:

public static A createA() throws XXXX, YYYY {
    try {
        return somethingThatThrows();
    } catch (InterruptedException | ExecutionException e) {
        Throwable throwable = e.getCause();
        if (throwable instanceOf XXXX) {
            throw (XXXX) throwable;
        } else if (e instance of YYYY) {
            throw (YYYY) throwable;
        } else if (throwable != null) {
            throw new RuntimeException(throwable);
        } else {
            throw new RuntimeException(e);
        }
    }
}         

Есть много таких create методов (каждый из которых возвращает свой тип). Для каждого из этих методов существует копия этой обработки исключения (т. Е. Она дублируется). Я надеюсь, что есть способ избежать всего этого идентичного кода и разместить эту логику только в одном месте.

Конечно, без обработки исключений вы просто извлекаете логику вспомогательной функции, и дублирование решается - тот факт, что здесь есть обработка исключений, делает ее другой. Следующий код не строит:

public static void helper(final Exception e) {
    Throwable throwable = e.getCause();
        if (throwable instanceOf XXXX) {
            throw (XXXX) throwable;
        } else if (e instance of YYYY) {
            throw (YYYY) throwable;
        } else if (throwable != null) {
            throw new RuntimeException(throwable);
        } else {
            throw new RuntimeException(e);
        }
}  

public static A createA() throws XXXX, YYYY {
    try {
        return somethingThatThrows();
    } catch (InterruptedException | ExecutionException e) {
        handle(e);
    }
}         

У кого-нибудь есть предложения?


person sabee    schedule 30.01.2018    source источник
comment
Этот вопрос никоим образом не дублирует уже упомянутые вопросы. Пожалуйста, снимите флаг.   -  person Loganathan    schedule 31.01.2018


Ответы (2)


Это можно сделать функциональным способом, как показано ниже:

@FunctionalInterface
interface SomethingThatThrows<T> {
    T execute() throws XXXX, YYYY, InterruptedException,ExecutionException;
}

private static <T> T handledFuntion(SomethingThatThrows<T> function) throws XXXX, YYYY {
    try {
        return function.execute();
    } catch (InterruptedException | ExecutionException e) {
        Throwable throwable = e.getCause();
        if (throwable instanceof XXXX) {
            throw (XXXX) throwable;
        } else if (e instanceof YYYY) {
            throw (YYYY) throwable;
        } else if (throwable != null) {
            throw new RuntimeException(throwable);
        } else {
            throw new RuntimeException(e);
        }
    }
}

// Use lambda literal - may be better when arguments are involved
public A createA(String arg1) throws XXXX, YYYY {
   return handledFuntion(() -> {
         // write code just like you'd write it in try{} body - 
         // all arguments to createA() are available
         return new A(arg1);
     });
}

// use a method handle, works best when there are no arguments
public B createB() throws XXXX, YYYY {
       return handledFuntion(this::somethingThatMakesB);
}


private B somethingOtherThatMakesB() throws XXXX, YYYY, InterruptedException,ExecutionException {
    // Some logic that creates and returns B
}

Изменить: объединенный ответ @Arkadiy.

person Loganathan    schedule 30.01.2018
comment
Спасибо! Есть ли способ сделать это, если методы создания имеют разные типы параметров? - person sabee; 30.01.2018
comment
Кроме того, это статические фабричные методы! Можно ли handledFunction вызвать из статического фабричного метода? - person sabee; 30.01.2018
comment
@sabee Ссылки на методы - это удобная форма, которую всегда можно заменить лямбда-выражениями, с помощью которых мы можем вызывать любой метод или запускать любой оператор. При использовании лямбда-выражения необходимо убедиться, что в выражении должен быть код, который не может вызывать никаких исключений или генерировать только исключение, как в функциональном интерфейсе, и не должен вызывать никаких дополнительных проверенных исключений. Если желательно, чтобы генерировалось больше исключений, сделайте функциональный интерфейс соответствующим. И служебный метод тоже можно сделать статическим. - person Loganathan; 31.01.2018

Попробуйте извлечь общую логику в частный метод и вызвать его:

public static A createA() throws XXXX, YYYY {
    try {
        return somethingThatThrows();
    } catch (InterruptedException | ExecutionException e) {
       processInterruptedExcutionExceptions(e);
    }
    return null;
}

private static void processInterruptedExcutionExceptions(final Exception e) throws XXXX, YYYY {
        Throwable throwable = e.getCause();
        if (throwable instanceOf XXXX) {
            throw (XXXX) throwable;
        } else if (e instance of YYYY) {
            throw (YYYY) throwable;
        } else if (throwable != null) {
            throw new RuntimeException(throwable);
        } else {
            throw new RuntimeException(e);
        }
}
person Dmytro Maslenko    schedule 30.01.2018
comment
Привет, спасибо за ответ. Я должен был упомянуть, что пробовал это - когда я это делаю, я получаю ошибку во всех методах создания. Ошибка отсутствует оператор возврата. - person sabee; 30.01.2018
comment
Не могли бы вы обновить исходный пост по коду, который вы пробовали? - person Dmytro Maslenko; 31.01.2018
comment
Сделанный! Дайте мне знать, если у вас есть еще какие-либо мысли по этому поводу - я ценю помощь. - person sabee; 31.01.2018
comment
В createA () добавьте return null. - person Dmytro Maslenko; 31.01.2018