能否通过抛出IllegalArgumentException并捕获以减少代码重复?
先看你的问题背景:
我目前有一段处理API数据的循环代码,存在调用
processAndStoreData的重复逻辑:for (int i = 0; i < dataFromApi.size(); i++){ try { returnedPoolData = dataFromApi.get(i).get(); if(returnedPoolData == null || returnedPoolData.getJsonString().isEmpty()) this.processAndStoreData(returnedPoolData, i, calledApis, id, true); else this.processAndStoreData(returnedPoolData, i, calledApis, id, false); } catch (ExecutionException e){ log.info("" + e); this.processAndStoreData(returnedPoolData, i, calledApis, id, true); } }为消除重复,我计划抛出
IllegalArgumentException并在catch块中处理,改写后的代码如下:for (int i = 0; i < dataFromApi.size(); i++){ try { returnedPoolData = dataFromApi.get(i).get(); if(returnedPoolData == null || returnedPoolData.getJsonString().isEmpty()) throw new IllegalArgumentException("Returned data was empty."); else this.processAndStoreData(returnedPoolData, i, calledApis, id, false); } catch (ExecutionException e){ log.info("" + e); this.processAndStoreData(returnedPoolData, i, calledApis, id, true); } }请问该方案是否可行?另外我发现移除
ExecutionException后,throw new语句不再出现红色下划线,这是为什么?
一、你的重构方案可行吗?
直接说结论:当前版本的方案不可行,因为它达不到你想要的效果。
你原本的意图是把空数据/空JSON的情况和ExecutionException的情况都统一到调用processAndStoreData(..., true)的逻辑里,但现在的代码有个致命问题:你抛出的IllegalArgumentException是运行时异常,而你的catch块只捕获ExecutionException,所以这个异常会直接向上抛出,导致程序中断,根本不会走到processAndStoreData的错误处理逻辑——完全违背了你的初衷。
如果想用异常的思路来优化,你需要把IllegalArgumentException也加入catch块,比如:
for (int i = 0; i < dataFromApi.size(); i++){ try { returnedPoolData = dataFromApi.get(i).get(); if(returnedPoolData == null || returnedPoolData.getJsonString().isEmpty()) throw new IllegalArgumentException("Returned data was empty."); this.processAndStoreData(returnedPoolData, i, calledApis, id, false); } catch (ExecutionException | IllegalArgumentException e){ if (e instanceof ExecutionException) { log.info("" + e); } this.processAndStoreData(returnedPoolData, i, calledApis, id, true); } }
不过更推荐的是不用异常处理预期的业务情况——空数据/空JSON属于你能预见的场景,用布尔变量标记状态会更清晰,代码可读性也更高,还能避免滥用异常:
for (int i = 0; i < dataFromApi.size(); i++){ boolean isErrorCase = false; try { returnedPoolData = dataFromApi.get(i).get(); if(returnedPoolData == null || returnedPoolData.getJsonString().isEmpty()) { isErrorCase = true; } else { this.processAndStoreData(returnedPoolData, i, calledApis, id, false); } } catch (ExecutionException e){ log.info("" + e); isErrorCase = true; } // 统一处理错误情况 if (isErrorCase) { this.processAndStoreData(returnedPoolData, i, calledApis, id, true); } }
这样既消除了重复代码,逻辑也一目了然,还没有异常带来的额外开销。
二、为什么移除ExecutionException后,throw语句的红色下划线消失了?
这个红色下划线的根源其实不是throw语句本身,而是变量未初始化的编译错误:
在你保留catch (ExecutionException e)的代码里,dataFromApi.get(i).get()可能抛出ExecutionException,这时候returnedPoolData还没被赋值就进入了catch块,而你在catch块里调用processAndStoreData(returnedPoolData, ...)时,Java编译器会报错:这个变量可能还没被初始化。IDE通常会把这个错误标记在try-catch块的相关位置,刚好显示在throw语句附近,所以你看到了红色下划线。
当你移除ExecutionException的catch块后,这个catch块不存在了,也就不会有在catch块里使用未初始化变量的情况,对应的编译错误自然消失,throw语句的红色下划线也就跟着不见了。
内容的提问来源于stack exchange,提问作者Maciaz

