Java组合多异常API时,无法使用多捕获构造器的优雅解决方案
哥们,我太懂这种被第三方API异常折磨的感觉了——两个API的异常体系不兼容,还没法改源码,只关心错误码却要写一堆重复的catch逻辑。结合你的需求(只关心是否失败、统一错误码,不纠结具体API),给你几个更优雅的解决方案:
方案1:用函数式接口+静态工厂实现类型安全的统一异常
这个方案核心是用函数式接口抽象“提取错误码”的能力,避开Java不支持多捕获参数类型的限制,同时保证类型安全:
首先定义自己的统一错误码枚举(把两个API的枚举映射过来,因为它们的int值一致):
// 自定义统一错误码枚举,对应两个API的int值 enum UnifiedErrorCode { BAD_REQUEST(400), NOT_FOUND(404), // 其他错误码... private final int code; UnifiedErrorCode(int code) { this.code = code; } // 根据int值获取统一枚举的方法 public static UnifiedErrorCode fromInt(int code) { for (UnifiedErrorCode ec : values()) { if (ec.code == code) { return ec; } } throw new IllegalArgumentException("Unknown error code: " + code); } }
然后定义一个函数式接口,用来提取不同异常的错误码:
@FunctionalInterface interface ErrorCodeExtractor<E extends Exception> { // 提取异常中的枚举,返回对应的统一错误码 UnifiedErrorCode extract(E exception); }
接下来写统一的异常包装类,用静态工厂方法处理每种API异常:
class UnifiedApiException extends Exception { private final UnifiedErrorCode errorCode; // 私有构造,只通过静态工厂创建 private <E extends Exception> UnifiedApiException(E cause, ErrorCodeExtractor<E> extractor) { super(cause); this.errorCode = extractor.extract(cause); } // 处理Api1Ex的静态工厂 public static UnifiedApiException wrapApi1(Api1Ex cause) { return new UnifiedApiException(cause, ex -> UnifiedErrorCode.fromInt(ex.getCode().getValue()) // 假设Api1Ex的枚举有getValue()返回int ); } // 处理Api2Ex的静态工厂 public static UnifiedApiException wrapApi2(Api2Ex cause) { return new UnifiedApiException(cause, ex -> UnifiedErrorCode.fromInt(ex.getCode().getCodeValue()) // 假设Api2Ex的枚举有getCodeValue()返回int ); } public UnifiedErrorCode getErrorCode() { return errorCode; } }
使用的时候就清晰多了,而且类型安全:
Api2Data doSomethingOrThrow() throws UnifiedApiException { try { byte[] data = api1.getData(); return api2.handleData(data); } catch (Api1Ex e) { throw UnifiedApiException.wrapApi1(e); } catch (Api2Ex e) { throw UnifiedApiException.wrapApi2(e); } }
这个方案的优势:
- 完全类型安全,不会出现非法异常类型的问题
- 扩展方便,后续加新API只需要加对应的静态工厂方法
- 错误码映射逻辑集中,便于维护
方案2:反射简化统一异常(适合快速实现)
如果两个API的错误码枚举int值完全一致,且不想写太多映射代码,可以用反射来提取错误码,这样就能直接用多捕获语法:
class UnifiedApiException extends Exception { private final UnifiedErrorCode errorCode; private UnifiedApiException(Exception cause) { super(cause); try { // 反射调用getCode方法获取枚举 Method getCodeMethod = cause.getClass().getMethod("getCode"); Enum<?> apiEnum = (Enum<?>) getCodeMethod.invoke(cause); // 从枚举获取int值(假设枚举有返回int的方法,比如getCode()) Method getIntMethod = apiEnum.getClass().getMethod("getCode"); int code = (int) getIntMethod.invoke(apiEnum); this.errorCode = UnifiedErrorCode.fromInt(code); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Unsupported exception type", e); } } public static UnifiedApiException wrap(Exception cause) { if (!(cause instanceof Api1Ex || cause instanceof Api2Ex)) { throw new RuntimeException("Unsupported exception type", cause); } return new UnifiedApiException(cause); } public UnifiedErrorCode getErrorCode() { return errorCode; } }
使用时就能直接用多捕获:
Api2Data doSomethingOrThrow() throws UnifiedApiException { try { byte[] data = api1.getData(); return api2.handleData(data); } catch (Api1Ex | Api2Ex e) { throw UnifiedApiException.wrap(e); } }
⚠️ 注意:反射会带来轻微的性能开销,而且如果后续API的getCode方法签名变更,这段代码会失效,适合API稳定的场景。
方案3:用Optional优化“自行恢复”的场景
对应你之前的somethingElse方法,用Optional替代返回null,代码更清晰,符合现代Java风格:
Optional<Api2Data> somethingElse() { try { byte[] data = api1.getData(); return Optional.of(api2.handleData(data)); } catch (Api1Ex e) { // 处理Api1异常逻辑:日志、重试等 log.error("Api1 failed, code: {}", UnifiedApiException.wrapApi1(e).getErrorCode()); } catch (Api2Ex e) { // 处理Api2异常逻辑 log.error("Api2 failed, code: {}", UnifiedApiException.wrapApi2(e).getErrorCode()); } return Optional.empty(); }
调用方可以通过ifPresent、orElse等方法处理结果,不用再判断null。
内容的提问来源于stack exchange,提问作者Rafael T
相关产品推荐
相关产品推荐

