使用apache commons对付checked exception

2019.10.04

众所周知,Java中的Checked Exception必须被显式地捕获或者传递,编译器会强制要求写入try catch块或在函数处进行声明。一般而言,业务上可预见的,应当被捕获处理的可设置为Checked Exception,如IOException,用户登录时没输密码抛出的异常等。

static void writeToFile(Integer integer) throws IOException {
    // logic to write to file which throws IOException
}

相应地,继承自RuntimeException的异常应理解为难以预见或对系统正常运行有重大影响的异常。如hupu-foundation中设置CommonSysException继承自RuntimeException,而CommonBizException继承自Exception作为Checked Exception.

必须承认,Java的Checked Exception设计并不合理。Java中的Lambda表达式所用到的函数式接口如Consumer、Function并未声明throws Exception。这导致forEach等操作时,内部的表达式不能抛出Checked Exception,必须原地处理。

要想在Lambda表达式中使用异常,或者是完全使用RuntimeException,或者是手动包装成RuntimeException. 即手动声明一个能够抛出异常的函数式接口,并提供一个方法使得能够接受一个抛出异常的表达式,返回一个正常的内置Lambda Function中。可参见这里

为避免每次都要手动写对应的wrapper,我们可以使用较新版本的apache commons中的Functions帮助我们进行包装。实际效果也是将Checked Exception包装成RuntimeException.如下所示。

// statsResultSet.rowSet:List<List<String>>
statsResultSet.getRowSet().forEach(eachList -> Functions.accept((innerList) -> {
	if (innerList.size() != headers.size()) {
		throw new CommonBizException(CommonErrorCode.NOT_FOUND.getErrorCode(),
		String.format("一个字符串",
			statsResultSet.getHeaders().size(), statsResultSet.getRowSet().get(0).size()));
	}
}, eachList));

使用上述方法前,应仔细考虑此处的异常到底是可预见的业务异常还是对系统运行有重大影响的RuntimeException,或许需要用到包装的地方并不多。