摘自Cay Horstmann的《Java 8 for the impatient》一书:
你不是一直讨厌你必须处理检查吗 可运行中的例外?编写一个捕获所有的方法
uncheck
选中的异常并将其转换为未检查的异常。为 例new Thread(uncheck( () -> { System.out.println("Zzz"); Thread.sleep(1000); })).start(); // Look, no catch (InterruptedException)!
提示:定义一个接口
RunnableEx
其run
方法可能会抛出任何 异常。然后实施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
兼容,这将消除使用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
的隐式转换。所以你可以使用它,但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的问题都是相关的:
- 如何包装已检查的异常,但在 Java 中保留原始运行时异常
- 将选中的异常替换为运行时异常?
- 在 Java 中处理运行时异常
但一个简单的方法是:
throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
最后,为了回答Callable<Void>
问题,我假设你的意思是"而不是Runnable
"?如果是这样,那是因为Thread
中的构造函数将Runnable
作为参数。