Class ScxFunctionException

All Implemented Interfaces:
Serializable

public final class ScxFunctionException extends RuntimeException

ScxFunctionException 是用于隔离执行者异常和执行目标异常的标记异常. 用于解决 "执行与被执行者之间的异常边界问题".

设计动机

设想一下有如下方法

public void read(Function1Void<byte[], Exception> bytesConsumer, int length) throws IOException {
    // someCode
}

当我们在调用时就会遇到如下问题, 也就是异常来源的模糊性问题.

try {
    read(bytes -> {
        // someCode maybe throw IOException
    }, 1024);
} catch (IOException e) {
    // 该如何区分这个 IOException 是 read 本身的 还是 bytesConsumer 的 ?
    e.printStackTrace();
}

针对这种情况 我们创建 ScxFunctionException 用于包装 如 高阶函数中由于 用户传递的 Function 所引发的异常.

当然 ScxFunctionException 不仅仅局限于函数式接口, 也适用于任何 "可动态修改, 由外部传入, 可能抛异常" 的可执行单元, 比如 装饰器模式中的被装饰者 (如 InputStream) 等 .

用法展示, 这里我展示 三种推荐用法 .

用法 1 : 包装所有异常.
public void read(Function1Void<byte[], Exception> bytesConsumer, int length) throws ScxFunctionException, IOException {
    // someCode
    try {
        bytesConsumer.apply(new byte[]{1,2,3});
    } catch (Exception e) {
        throw new ScxFunctionException(e);
    }
    // someCode
}

这种用法的好处是我们永远不会错误的判断异常的来源, 不过缺点则是对于 read 的调用方, 异常堆栈可能总是很长.

用法 2 : 只包装可能引发混淆的异常, 同时允许穿透传递所有 运行时异常. 这种用法更适用于 Function 只抛出 运行时异常.
// 假设 NoPermissionException 是一个 RuntimeException.
public void checkPermission(Function0<String[], RuntimeException> permissionsSupplier, String department) throws ScxFunctionException, NoPermissionException {
    // someCode
    try {
        permissionsSupplier.apply();
    } catch (NoPermissionException e) {
        // 只包装可能混淆的异常
        throw new ScxFunctionException(e);
    } catch (RuntimeException e) {
        // 此处的 catch 代码块也可以直接删除, 此处为了演示.
        throw e;
    }
    // someCode
}

这种方法的好处是对于 permissionsSupplier 中发生的 不会混淆的运行时异常 (比如空指针, 索引越界等). 会直接穿透到 checkPermission 的调用者, 在异常层级上会更干净, 而且更符合所谓的函数式哲学. 缺点是模糊了部分异常的层级, 在某些情况下可能不太适合.

用法 3 : 类似 用法 2, 只包装可能引发混淆的异常, 但允许泛型异常. 这样便不仅仅局限于 只能抛出运行时异常, 也能抛出受检异常 .
public <X extends Exception> void read(Function1Void<byte[], X> bytesConsumer, int length) throws X, ScxFunctionException, IOException {
    // someCode
    try {
        bytesConsumer.apply(new byte[]{1,2,3});
    } catch (Exception e) {
        // 包装易混淆异常
        if(e instanceof IOException) {
            throw new ScxFunctionException(e);
        }
        // 其他异常直接抛出
        throw e;
    }
    // someCode
}

小提示: 在实际使用过程中 X 会被推断为 bytesConsumer 抛出的所有异常的最小共同父类.

Version:
0.0.1
Author:
scx567888
See Also:
  • Constructor Details

    • ScxFunctionException

      public ScxFunctionException(Throwable cause)
      作为一个标记类 我们只保留一个构造函数, 同时不允许传入 null
  • Method Details

    • getRootCause

      public Throwable getRootCause()
      获取真正的异常