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 актуальны:

  1. Как обернуть проверенные исключения, но сохранить исходные исключения во время выполнения в Java
  2. Заменить проверенное исключение на исключение во время выполнения?
  3. Обработка исключений во время выполнения в Java

Но простой способ это:

throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);

Наконец, чтобы ответить на Callable<Void> вопрос, я предполагаю, что вы имеете в виду "вместо Runnable "Если так, то это потому, что конструкторы из Thread взять Runnable в качестве аргумента.

Другие вопросы по тегам