在 Java 中从 toString 方法捕获异常

Catch exception from toString method in Java

最近我在 GitHub 上看到了以下代码:

private static String safeToString(Object obj) {
    if (obj == null) return null;
    try {
        return obj.toString();
    } catch (Throwable t) {
        return "Error occured";
    }
}

我从未在 try-catch 块中放置 toString() 方法调用。但现在想想,这可能是有道理的。例如,有人可以在 class 中覆盖 toString() 方法,这可能会引发运行时异常,例如 NullPointerException。所以我们可以尝试捕捉Exception。但为什么 Throwable?你觉得有道理吗?

ThrowableExceptionError 的父 class。 尝试捕获 Error 通常不是一个好主意,因为它被设计为不被捕获。

赶上 Throwable 只是赶上 Exception 的超预期和适得其反的版本。尽管如此,如果 出于某种原因 你创建了另一种 Throwable 你想要与 Exception 一起使用,这可能是一种在单个try/catch 块。并不是说这样做是一种干净的方法,但它会起作用。

编辑 TL;DR :在大多数情况下,捕获 Exception 而不是 Throwable

在极少数情况下,您可能想要捕获这样的错误。然而,总的来说这是一个坏主意,在这种情况下它可能有意义,因为这通常用于 logging/debugging 目的而不是直接由应用程序使用。

我希望能提供更多信息,例如

private static String safeToString(Object obj) {
    if (obj == null) return null;
    try {
        return obj.toString();
    } catch (Throwable t) {
        return obj.getClass() + ".toString() threw " + t;
    }
}

例如

class Element {
    Object data;
    Element e;

    public String toString() {
        return data + (e == null ? "" : e.toString());
    }
}

Element e = new Element();
e.data = "hi";
e.e = e; // oops

System.out.println("e: " + safeToString(e)); // doesn't kill the thread or JVM.

捕获任何 Throwable 然后继续执行是不正确的,因为它包含 Error,这意味着是致命的:

来自Javadocs:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.

也就是一些Error可以恢复(比如LinkageError),其他的就没那么多了

但是捕捉 Exception 可能是一个有效的用例,例如在您不希望执行仅仅因为调用 toString() 失败:

private static String safeToString(Object obj) {
    try {
        return obj == null ? "null" : obj.toString();
    } catch (Exception e) {
        return "<exception: " + e + ">";
    }
}

几乎没有理由这样做。 contract of toString() 并没有说允许从该方法中抛出异常。任何抛出异常的代码都是损坏的代码,这种异常需要暴露和修复,而不是压制。

如果您要将一些不受您控制的库中的“坏”对象转换为字符串,那么写 catch (RuntimeExcepton e) 可能是合适的,但是这样的捕获应该伴随详细说明为什么需要的评论,因为一般情况下不需要。

抛开流氓异常抛出 toString 方法,请注意 Java 已经有至少两种“安全”方法将可能为 null 的值转换为字符串:

  • Objects.toString(obj, null)
  • String.valueOf(obj)

...所以我会质疑 safeToString 方法是否应该存在。