抛开检查异常和运行时异常的宗教争论,有很多次那些处理检查异常的例子的可怜构造类库就能让你发疯。
考虑下面你可能想要写的一小段代码:
public void createTempFileForKey(String key) {
Map<String, File> tempFiles = new ConcurrentHashMap<>();
//不编译,因为抛出了IOException
tempFiles.computeIfAbsent(key, k -> File.createTempFile(key, ".tmp"));
}
为了正常编译你需要捕获这个异常。代码如下:
public void createTempFileForKey(String key) {
Map<String, File> tempFiles = new ConcurrentHashMap<>();
tempFiles.computeIfAbsent(key, k -> {
try {
return File.createTempFile(key, ".tmp");
}catch(IOException e) {
e.printStackTrace();
return null;
}
});
}
尽管这段代码能正常编译,实际上IOException已经被吞掉了。这个方法的用户应该被通知已经抛出了一个异常。
为了解决这个你可以把IOException包装成一个范型的RuntimeException,如下:
public void createTempFileForKey(String key) throws RuntimeException {
Map<String, File> tempFiles = new ConcurrentHashMap<>();
tempFiles.computeIfAbsent(key, k -> {
try {
return File.createTempFile(key, ".tmp");
}catch(IOException e) {
throw new RuntimeException(e);
}
});
}
这段代码抛出了一个异常,但不是这段代码应该自然抛出的那个IOException。对那些支持RuntimeExceptions的人来说, 这段代码可能会让他们高兴。尤其解决方案是重新定义了一个自定义的IORuntimeException.不过大多数人编码的方式是,他们期望他们的方法能从File.createTempFile方法抛出检查时IOException.
这样做的自然方式有一点复杂,像这样:
public void createTempFileForKey(String key) throws IOException{
Map<String, File> tempFiles = new ConcurrentHashMap<>();
try {
tempFiles.computeIfAbsent(key, k -> {
try {
return File.createTempFile(key, ".tmp");
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}catch(RuntimeException e){
if(e.getCause() instanceof IOException){
throw (IOException)e.getCause();
}
}
}
从lamabda内部,你必须捕获异常,把它包装成一个RuntimeException并抛出这个RuntimeException.lambda必须捕获这个RuntimeException解包并重新抛出这个IOException.所有这些的确非常丑陋。
理想的做法是在lamabda内部抛出一个检查异常,不用改变computeIfAbsent的声明。换句话说,如果是一个运行时异常就抛出一个检查异常。但不幸的是Java不会让我们这样做。
除非我们作弊。下面的两个方法精确的做到了我们想要的,假如是一个运行时异常就抛出一个检查异常。
public static void main(String[] args){
doThrow(new IOException());
}
static void doThrow(Exception e) {
CheckedException.<RuntimeException> doThrow0(e);
}
static <E extends Exception>
void doThrow0(Exception e) throws E {
throw (E) e;
}
注意我们创建并抛出了一个没有在main方法里声明的IOException。
public static void main(String[] args){
getUnsafe().throwException(new IOException());
}
private static Unsafe getUnsafe(){
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
我们又一次设法抛出了一个没有在其方法里声明的IOException。不管你更喜欢哪个方法,我们现在可以用这种方式自由地写原始代码。
public void createTempFileForKey(String key) throws IOException{
Map<String, File> tempFiles = new ConcurrentHashMap<>();
tempFiles.computeIfAbsent(key, k -> {
try {
return File.createTempFile(key, ".tmp");
} catch (IOException e) {
throw doThrow(e);
}
});
}
private RuntimeException doThrow(Exception e){
getUnsafe().throwException(e);
return new RuntimeException();
}
private static Unsafe getUnsafe(){
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
doThrow()方法明显的包装在了一些工具类里,这样可以使你在createTempFileForKey()方法中的代码更加简洁。
原文地址:http://blog.csdn.net/supercrsky/article/details/45891741