Java 8 lambda catching exception



摘自Cay Horstmann的《Java 8 for the impatient》一书:

你不是一直讨厌你必须处理检查吗 可运行中的例外?编写一个捕获所有的方法uncheck选中的异常并将其转换为未检查的异常。为 例

new Thread(uncheck(
() -> { System.out.println("Zzz"); Thread.sleep(1000); })).start();
// Look, no catch (InterruptedException)!

提示:定义一个接口RunnableExrun方法可能会抛出任何 异常。然后实施

public static Runnable uncheck(RunnableEx runner)

在取消选中函数中使用 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?

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

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兼容,这将消除使用RunnableRunnableEx实例来表示单个操作的需要:

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,需要实现代码返回一个值。换句话说,没有从voidVoid的隐式转换。所以你可以使用它,但lambda表达式需要在其末尾有一个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();
}
}

这种从lambda中捕获异常的方法工作得很好,并且(有一些补充)是来自jOOλ(一个流行的Java库)的Unchecked类中实现的方法。

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

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

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

但一个简单的方法是:

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

最后,为了回答Callable<Void>问题,我假设你的意思是"而不是Runnable"?如果是这样,那是因为Thread中的构造函数将Runnable作为参数。

相关内容

  • 没有找到相关文章

最新更新