Don't Group Exception Catchers

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

有时候我们会重新抛出异常。在Java中,我们比其他语言更经常这样做,因为它具有检查异常。有时候我们必须捕获并重新抛出一个方法中来自不同位置的几个异常。Java 7引入了在单个catch块中对不同类型的异常进行分组。但即使没有分组,也可以只捕获IOException甚至Exception并为所有类型和所有来源(抛出异常的方法)提供一个catch块。最近我意识到这是一种不好的做法。下面是原因。

考虑以下Java方法(我正在使用Apache Commons IO):

byte[] read(String uri) {
  try {
    return IOUtils.toByteArray(
      new URL(uri).openStream()
    );
  } catch (IOException ex) {
    throw new IllegalArgumentException(ex);
  }
}

这还不完美,让我们重写一下,提供更多错误上下文,就像之前建议的那样:

byte[] read(String uri) {
  try {
    return IOUtils.toByteArray(
      new URL(uri).openStream()
    );
  } catch (IOException ex) {
    throw new IllegalArgumentException(
      String.format(
        "Failed to read from '%s'",
        uri
      ),
      ex
    );
  }
}

在这里,异常可能会在三个地方被抛出:

无论是谁抛出异常,我们都在同一个“catch”块中捕获,然后用相同的消息重新抛出。我认为这样做是不好的,因为重新抛出异常提供的错误上下文对发生的问题关注较少。

我建议进行这种重构(我没有关闭输入流,这是错误的,但这是一个单独讨论的话题)。

byte[] read(String uri) {
  URL url;
  try {
    url = new URL(uri);
  } catch (MalformedURLException ex) {
    throw new IllegalArgumentException(
      String.format(
        "Failed to parse the URI '%s'",
        uri
      ),
      ex
    );
  }
  InputStream stream;
  try {
    stream = url.openStream();
  } catch (IOException ex) {
    throw new IllegalArgumentException(
      String.format(
        "Failed to open the stream for '%s'",
        uri
      ),
      ex
    );
  }
  try {
    return IOUtils.toByteArray(stream);
  } catch (IOException ex) {
    throw new IllegalArgumentException(
      String.format(
        "Failed to read the stream for '%s'",
        uri
      ),
      ex
    );
  }
}

这段代码更长,但同时更便于调试、测试和在生产模式中使用。catch块能更好地解释情况,并在重新抛出的异常中提供更好的上下文,因为它只处理单个情况。

因此,我建议的规则是:如果捕获到异常,每个发起者都必须有自己的catch块。

显然,我认为在单个catch块中分组异常类型是一种不良实践。

Translated by ChatGPT gpt-3.5-turbo/35 on 2023-09-08 at 16:28

sixnines availability badge   GitHub stars