3

来自Cay Horstmann的《不耐烦的 Java 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 函数中使用 lambda 表达式。

我为此编写了这样的代码:

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?

4

2 回答 2

3

您的代码无法将已检查的异常包装到未检查的异常中。此外,它不遵循使用 lambda 表达式的提示。一个更正的版本是:

public static Runnable uncheck(RunnableEx r) {
    return () -> {
        try {
            r.run();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

确实有进一步改进的可能性。由于RunnableEx接口的全部目的是包装 a 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应创建 a 而不是 a的标记Runnable。使用此接口定义,您可以通过

public static void main(String[] args) {
    new Thread( (RunnableEx) () -> {
            System.out.println("Zzz");
            Thread.sleep(1000);
        }
    ).start();
}

你不能在Callable<Void>这里“只使用” a 的原因是它Callable<Void>仍然是 a Callable,要求实现代码返回一个值。换句话说,没有从voidto的隐式转换Void。所以你可以使用它,但是 lambda 表达式需要return null;在其末尾声明一个 then (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();
    }
}
于 2017-01-30T12:55:44.707 回答
1

这种从 lambdas 中捕获异常的方法效果很好,并且(有一些补充)是在流行的 Java 库jOOλUnchecked的类中实现的方法。

为了进行比较,请参阅它们的静态方法Unchecked.runnable,它与您的解决方案相同,除了它们的Exception处理程序和 lambda 缩写返回值。

关于从Java 8 到 Impatients的分配,您的uncheck方法不使用 lambda(为此,请参阅Unchecked.runnable方法),并且不会引发未经检查的异常。为此,您的catch块(当前为空)需要将异常包装到未经检查的异常中。有很多方法可以做到这一点,许多关于 SO 的问题都是相关的:

  1. 如何包装检查的异常但在 Java 中保留原始的运行时异常
  2. 用运行时异常替换检查异常?
  3. 在 Java 中处理运行时异常

但一个简单的方法是:

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

最后,为了回答这个Callable<Void>问题,我假设您的意思是“而不是Runnable”?如果是这样,那是因为来自的构造函数Thread将 aRunnable作为参数。

于 2017-01-29T02:44:59.847 回答