如何用Java 8 Optional优化空检查及DAO调用代码?
优化方案:用Optional重构你的代码
第一步:修正selectById方法
你的初步尝试存在隐患:如果logs为空,logs.get(0)会直接抛出IndexOutOfBoundsException,而非返回Optional.empty()。既然你确定selectUserLogs返回的List永远非null,用Stream的findFirst()能更安全地生成Optional:
public Optional<UserLog> selectById(String userid) throws DataException { List<UserLog> logs = selectUserLogs(SQL_SELECT_BY_ID, userid); return logs.stream().findFirst(); }
列表为空时,该方法会直接返回Optional.empty(),既避免了索引越界问题,代码也更简洁符合语义。
第二步:优化调用方callingMethod
原调用代码有两个明显问题:一是重复调用selectById(userid)会触发两次数据库查询,浪费资源;二是嵌套null检查冗余,可读性差。用Optional的链式API可以完美解决:
基础优化版
public void callingMethod() { // 仅查询一次,避免重复操作 Optional<UserLog> userLogOpt = userLogDao.selectById(userid); if (starttime != null && FIVE_HOUR <= durationSinceStartTime() && userLogOpt.isPresent() && userLogOpt.get().getStartTime() != null) { log.warn("Logs came in late"); } }
更贴合Optional最佳实践的版本
用map方法简化空检查逻辑,让代码更流畅:
public void callingMethod() { boolean shouldWarn = starttime != null && FIVE_HOUR <= durationSinceStartTime() && userLogDao.selectById(userid) .map(UserLog::getStartTime) .isPresent(); if (shouldWarn) { log.warn("Logs came in late"); } }
这里map(UserLog::getStartTime)会把Optional<UserLog>转换为对应getStartTime返回值的Optional——如果UserLog不存在或getStartTime返回null,都会变成Optional.empty(),最后用isPresent()统一判断条件。
额外建议
- 尽量消除嵌套null:如果
getStartTime()也可能返回null,建议把它的返回类型改成Optional<LocalDateTime>(假设是时间类型),这样可以用flatMap替代map,彻底消除空检查:// 假设UserLog的getStartTime返回Optional<LocalDateTime> boolean shouldWarn = starttime != null && FIVE_HOUR <= durationSinceStartTime() && userLogDao.selectById(userid) .flatMap(UserLog::getStartTime) .isPresent(); - 遵循《Clean Code》原则:用Optional明确表达方法返回值可能为空的语义,让调用方一眼就能知晓如何处理空值,从根源避免NPE。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

