Java:反射创建数组时抛出IllegalArgumentException的最优实现方式
反射创建数组时抛出指定IllegalArgumentException的最优实现
嘿,我来帮你梳理这个问题~首先明确说:完全可以用try-catch块实现,这完全贴合你的计划;不过还有更高效的前置校验方案,能提前规避异常,咱们一步步拆解:
一、贴合你思路的try-catch实现
如果你打算先尝试初始化数组,再捕获异常包装成目标IllegalArgumentException,代码可以这么写:
public <T> T[] combine(T[] arr1, T[] arr2, Comparator<T> cmp, Class<?> type) throws IllegalArgumentException { // 先做基础非空校验(可选但非常推荐,提前拦截明显错误) if (arr1 == null || arr2 == null || cmp == null || type == null) { throw new IllegalArgumentException("所有参数都不能为null"); } T[] newArray; try { // 尝试创建合并后的数组 newArray = (T[]) Array.newInstance(type, arr1.length + arr2.length); } catch (NegativeArraySizeException | IllegalArgumentException | ClassCastException e) { // 捕获创建数组时可能出现的核心异常,包装成我们需要的类型抛出 // 把原异常作为cause传入,方便后续排查问题根源 throw new IllegalArgumentException("创建合并数组失败", e); } // 接下来就是数组合并+排序的逻辑 System.arraycopy(arr1, 0, newArray, 0, arr1.length); System.arraycopy(arr2, 0, newArray, arr1.length, arr2.length); Arrays.sort(newArray, cmp); return newArray; }
这里要注意捕获的几个异常类型:
NegativeArraySizeException:当两个数组长度相加溢出成负数时会抛这个(比如俩数组都是Integer.MAX_VALUE长度)IllegalArgumentException:Array.newInstance本身就可能抛这个,比如传入的type是void类型,或者是和泛型T不兼容的类型ClassCastException:强制转(T[])失败时抛出,比如type和输入数组的元素类型不匹配
把这些异常都捕获,统一包装成你需要的IllegalArgumentException,还能保留异常根源,方便调试。
二、更优的前置校验方案(提前避坑)
其实在调用Array.newInstance之前,我们可以先做一波校验,提前把明显的错误拦下来,比事后捕获异常更高效,也更符合防御式编程的思路:
public <T> T[] combine(T[] arr1, T[] arr2, Comparator<T> cmp, Class<?> type) throws IllegalArgumentException { // 1. 逐个校验参数非空,给出明确错误信息 if (arr1 == null) throw new IllegalArgumentException("arr1参数不能为null"); if (arr2 == null) throw new IllegalArgumentException("arr2参数不能为null"); if (cmp == null) throw new IllegalArgumentException("比较器cmp不能为null"); if (type == null) throw new IllegalArgumentException("数组元素类型type不能为null"); // 2. 校验长度之和是否合法,避免溢出成负数 int totalLength = arr1.length + arr2.length; if (totalLength < 0) { throw new IllegalArgumentException("两个数组长度之和溢出,无法创建足够大的数组"); } // 3. 校验type是否是合法的数组元素类型 if (type == void.class) { throw new IllegalArgumentException("不能用void类型创建数组"); } // 校验type和输入数组的元素类型是否兼容(比如避免基本类型和包装类不匹配的问题) Class<?> arrComponentType = arr1.getClass().getComponentType(); if (!type.isAssignableFrom(arrComponentType) && !arrComponentType.isAssignableFrom(type)) { throw new IllegalArgumentException("type类型与输入数组元素类型不兼容"); } // 4. 最后尝试创建数组,此时大概率不会出问题,但可以保留兜底的try-catch try { T[] newArray = (T[]) Array.newInstance(type, totalLength); // 合并数组并排序 System.arraycopy(arr1, 0, newArray, 0, arr1.length); System.arraycopy(arr2, 0, newArray, arr1.length, arr2.length); Arrays.sort(newArray, cmp); return newArray; } catch (Exception e) { // 兜底捕获意外出现的异常 throw new IllegalArgumentException("创建合并数组时发生意外错误", e); } }
这种方式的好处很明显:
- 提前拦截错误,给出的错误信息更具体,调用者一眼就能知道哪里错了
- 减少异常处理的开销(Java里异常处理是相对较重的操作)
- 代码逻辑更清晰,哪些参数不合法一目了然
三、几个关键注意点
- 不管用哪种方式,都建议把原异常作为cause传入新的
IllegalArgumentException,这样排查问题时能看到完整的异常栈,找到根源 - 别用空的
IllegalArgumentException()构造函数,一定要带上明确的错误信息,比如"数组长度之和溢出",比空异常有用太多 - 要保证所有可能的错误最终都转换成
IllegalArgumentException抛出,符合方法的异常约定
内容的提问来源于stack exchange,提问作者jdoe
相关产品推荐
相关产品推荐

