处理不能发生的异常的最佳方法

Best way to handle exceptions that can not occur

我使用以下代码构建一个 SQL 语句来更新特定对象所具有的字段的值。我的问题是,因为我检查对象是否有特定字段,所以永远不会发生异常。使此代码更具可读性的最佳方法是什么,例如避免使用 try {} catch {} 块。

 List<Field> modelFields = Arrays.asList(model.getClass().getFields());
 String updateString = "";

 for (Field field : fields) {
   if (modelFields.contains(field)) {
            try {
                updateString += " " + field.get((Object) model) + " ";
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            updateString += " NULL ";
        }
    }

如果您认为异常永远不应该发生,则抛出一个运行时异常来包装原始异常:

try {
    updateString += " " + field.get((Object) model) + " ";
} catch (IllegalAccessException e) {
    throw new IllegalStateException("this should never happen: the field is supposed to be public and thus accessible", e);
}

这样一来,如果您错了,或者如果代码发生了变化并且不可能的事情变成了可能,您将得到一个带有明确的错误消息和异常的根本原因的异常,而不是静静地忽略错误并稍后得到一个不相关的错误,或者更糟的是,一个有效但不正确的结果。

异常的全部意义在于我们不希望它们在大多数时候发生。如果构建一个健壮的大型应用程序,您应该始终以有意义的方式编写处理异常的代码,中止应用程序正在尝试的任何子任务,并以不依赖于任何成功代码的方式继续进行。

但是,如果您正在构建更轻量级的东西,可以在出现问题时退出,捕获并重新抛出已检查的异常作为未检查的异常(通常 RuntimeException)可以让您快速编码并不用担心检查异常会在您的代码中到处传播。

这不仅仅是不期望它现在发生的情况,而是在未来继续进行的良好实践。底层实现可能会发生变化,假设无法抛出异常的代码可能会突然变得能够抛出异常,这是由于其他 类 中所做的更改。最糟糕的事情是吞下异常;至少,确保将它们打印到日志 and/or 控制台。

我同意@JBNizet 的回答。但是,也许不是在所有情况下您都希望对不可能发生的事情进行例外处理。

我承认这可能与 OP 的预期略有不同,但我认为仍然有助于将正确答案放在更广泛的背景下。

参见下面的示例。

public class App {
  enum Which {ONE, TWO};
  String on(Which which) {
    switch (which) {
      case ONE: return "This is one";
      case TWO: return "This is another";
      default: throw new IllegalStateException("Missing case: "+which.name()+" of Which");
    }
  }
}

假设有人添加了第 3 个枚举值 THREE。这段代码将始终编译良好,并且编辑器(Netbeans/Eclipse)也永远不会将其标记为问题。您只会在 运行 时间内发现问题。

在这种情况下,我什至建议确保没有 default 定义明确抛出异常的情况。如果在 javac 或编辑器中选择了正确的设置,它至少会更早地被标记。在最坏的情况下,它会编译,但在 运行 时,你仍然会得到一个 RuntimeException:

线程 "main" java.lang.RuntimeException 中的异常:无法编译的源代码 - 缺少 return 语句

有点怀疑......'Uncompilable'在运行时间......

这仅适用于编译器或编辑器应该能够识别的绑定开关集,这意味着仅 Enum,而不适用于 String 或 int。对于这些情况,我可能会建议仍然为未发现的情况设置一个明确的例外。

啊 - 而且,我肯定会说在很多场景中可以避免切换案例......通过将案例直接绑定为枚举值的方法(覆盖抽象方法),或者通过实施 visitor pattern.

这里是如何重新实现枚举的:

public class App {
  enum Which {
    ONE { @Override String on() { return "This is One"; } }, 
    TWO { @Override String on() { return "This is Two"; } }, 
    THREE { @Override String on() { return "This is Three"; } }, 
    ;

    abstract String on();
  };

  static String on(Which which) {
    return which.on();
  }

  public static void main(String[] args) {
    String result = on(Which.THREE);
  }
}

现在我们已经完全避免了这整个场景......现在它总是归结为在编译时检测到的未实现的方法!另外它也快得多,因为不需要 switch/case 查找。

这可能是最终目标:

Prefer Compile Time Errors over Runtime Exceptions.