Java 8 лямбда ловить исключение
Из книги Ява 8 для нетерпеливых Кей Хорстманн:
Разве вы не всегда ненавидели то, что вам приходилось иметь дело с проверенными исключениями в Runnable? Напишите метод
uncheck
это ловит все проверенные исключения и превращает их в непроверенные исключения. Например,new Thread(uncheck( () -> { System.out.println("Zzz"); Thread.sleep(1000); })).start(); // Look, no catch (InterruptedException)!
Подсказка: определите интерфейс
RunnableEx
чьяrun
Метод может выдавать любые исключения. Затем реализоватьpublic static Runnable uncheck(RunnableEx runner)
Используйте лямбда-выражение внутри функции uncheck.
Для которого я кодирую так:
public interface RunnableEx {
void run() throws Exception;
}
public class TestUncheck {
public static Runnable uncheck(RunnableEx r) {
return new Runnable() {
@Override
public void run() {
try {
r.run();
} catch (Exception e) {
}
}
};
}
public static void main(String[] args) {
new Thread(uncheck(
() -> {
System.out.println("Zzz");
Thread.sleep(1000);
}
)).start();
}
}
Я сделал в соответствии с тем, что намекнули? Есть ли лучший способ сделать это?
Также есть еще один дополнительный вопрос:
Почему ты не можешь просто использовать
Callable<Void>
вместоRunnableEx?
2 ответа
Ваш код не может обернуть проверенное исключение в непроверенное. Кроме того, он не следует подсказке использовать лямбда-выражение. Исправленная версия будет:
public static Runnable uncheck(RunnableEx r) {
return () -> {
try {
r.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
Есть действительно возможности улучшить это дальше. Так как вся цель RunnableEx
Интерфейс должен обернуть Runnable
Вы можете поместить фабричный метод внутри самого интерфейса:
public interface RunnableEx {
void run() throws Exception;
public static Runnable uncheck(RunnableEx r) {
return () -> {
try {
r.run();
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
Конечно, теперь ваш код вызова должен быть адаптирован к:
public static void main(String[] args) {
new Thread(RunnableEx.uncheck(
() -> {
System.out.println("Zzz");
Thread.sleep(1000);
}
)).start();
}
Теперь сам интерфейс может реализовать функцию обтекания, став совместимым с Runnable
, что устранит необходимость иметь Runnable
и RunnableEx
экземпляр для представления одного действия:
public interface RunnableEx extends Runnable {
void runWithException() throws Exception;
@Override default void run() {
try {
runWithException();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Runnable uncheck(RunnableEx r) {
return r;
}
}
Обратите внимание, что хотя вызывающий код не изменяется синтаксически, он неявно создает Runnable
экземпляр в первую очередь, при создании RunnableEx
например, вот почему uncheck
становится неоперативным, просто служит маркером того, что RunnableEx
должен быть создан, а не Runnable
, С этим определением интерфейса вы можете достичь того же самого через
public static void main(String[] args) {
new Thread( (RunnableEx) () -> {
System.out.println("Zzz");
Thread.sleep(1000);
}
).start();
}
Причина, по которой вы не можете "просто использовать" Callable<Void>
вот это Callable<Void>
по-прежнему является Callable
, требуя, чтобы код реализации возвращал значение. Другими словами, нет неявного преобразования из void
в Void
, Таким образом, вы можете использовать его, но лямбда-выражение потребует return null;
заявление в концеnull
это единственное значение, совместимое с Void
).
public class TestUncheck {
public static Runnable uncheck(Callable<Void> r) {
return () -> {
try { r.call(); }
catch (Exception e) { throw new RuntimeException(e); }
};
}
public static void main(String[] args) {
new Thread(uncheck(
() -> {
System.out.println("Zzz");
Thread.sleep(1000);
return null;
}
)).start();
}
}
Этот способ отлова исключений из лямбд работает нормально, и он (с несколькими дополнениями) реализован в Unchecked
класс от jOOλ, популярной библиотеки Java.
Для сравнения посмотрите их статический метод Unchecked.runnable
, который идентичен вашему решению, за исключением их Exception
обработчик и лямбда, исключая возвращаемое значение.
Что касается назначения из Java 8 для нетерпеливых, ваш uncheck
метод не использует лямбду (для этого см. Unchecked.runnable
метод), и не выбрасывает непроверенное исключение. Для этого ваш catch
Блок (в настоящее время пустой) должен обернуть исключение в исключение без проверки. Есть много способов сделать это, и многие вопросы по SO актуальны:
- Как обернуть проверенные исключения, но сохранить исходные исключения во время выполнения в Java
- Заменить проверенное исключение на исключение во время выполнения?
- Обработка исключений во время выполнения в Java
Но простой способ это:
throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
Наконец, чтобы ответить на Callable<Void>
вопрос, я предполагаю, что вы имеете в виду "вместо Runnable
"Если так, то это потому, что конструкторы из Thread
взять Runnable
в качестве аргумента.