【问题标题】:How to handle a static final field initializer that throws checked exception如何处理抛出检查异常的静态最终字段初始化程序
【发布时间】:2010-12-24 10:02:15
【问题描述】:

我正面临一个用例,我想声明一个带有初始化语句的static finalfield,该语句被声明为抛出一个检查的异常。通常,它看起来像这样:

public static final ObjectName OBJECT_NAME = new ObjectName("foo:type=bar");

我在这里遇到的问题是 ObjectName 构造函数可能会抛出各种检查异常,我不在乎(因为我知道我的名字是有效的,如果它不是那么严重崩溃也没关系)。 java编译器不会让我忽略这个(因为它是一个检查异常),我不想诉诸:

public static final ObjectName OBJECT_NAME;
static {
    try {
        OBJECT_NAME = new ObjectName("foo:type=bar");
    } catch (final Exception ex) {
        throw new RuntimeException("Failed to create ObjectName instance in static block.", ex);
    }
}

因为静态块真的非常难以阅读。有没有人建议如何以一种好的、干净的方式处理这个案例?

【问题讨论】:

  • 我个人的解决方案是抛出一个CheckedExceptionsAreAPainInTheAssSometimesException,这是一个运行时异常。然后程序就会崩溃。

标签: java exception static final initializer


【解决方案1】:

如果您不喜欢静态块(有些人不喜欢),那么另一种选择是使用静态方法。 IIRC,Josh Bloch 推荐了这个(在快速检查时显然不在 Effective Java 中)。

public static final ObjectName OBJECT_NAME = createObjectName("foo:type=bar");

private static ObjectName createObjectName(final String name) {
    try {
        return new ObjectName(name);
    } catch (final SomeException exc) {
        throw new Error(exc);
    }  
}

或者:

public static final ObjectName OBJECT_NAME = createObjectName();

private static ObjectName createObjectName() {
    try {
        return new ObjectName("foo:type=bar");
    } catch (final SomeException exc) {
        throw new Error(exc);
    }  
}

(已编辑:更正第二个示例以从方法返回而不是分配 static。)

【讨论】:

  • 我没有考虑过,尽管现在我阅读了它,但我 100% 确定我很久以前就使用过这种方法。我会使用它,因为我不喜欢静态块,也喜欢我的代码可供初学者阅读(你永远不知道谁会在你自己之后维护你的代码:))。
  • 给我一个编译器错误“必须返回 ObjectName 类型的结果”——一个简单的解决方案是在 catch 块中包含 return null 吗?但是调试起来有点奇怪
  • 我认为您指的是第 59 条:避免不必要地使用已检查异常(Effective Java,第 2 版)。在该项目中,Bloch 建议引发异常的代码的作者考虑其客户端是否“一旦遇到异常就可以采取一些有用的行动”。这与这种情况不同,问题不是抛出异常的合法性,而是如何最好地处理异常。通过查看java.lang.Error 的文档,我觉得在这里抛出错误并不是最好的选择。
  • Errors 应该用于表示程序“严重问题”的“异常情况”。在 Bloch 给出的示例中,AssertionErrors 仅在不应该运行的代码中抛出,例如在switch 块的default 部分中,或在不可实例化类的私有构造函数中。恕我直言,鉴于这种情况下的异常情况并非不可能,因此抛出 RuntimeException 比抛出 Error 更合适。
  • @nullstellensatz 这是六年前的!我指的是关于为构造创建静态方法而不是实例初始化器的 Bloch 建议(可能在过去十年中发生了变化)。在 Kindle 上搜索 Effective Java 的第 2 版,它实际上并没有出现在那本书中。 / throw 异常的目的是表明该类已损坏并且它必须死亡。那是一个错误。 (将被包裹在java.lang.ExceptionInInitializerError 中。)Error 也比废话RuntimeException 更具可读性。
【解决方案2】:

您的代码完全有效。我觉得读起来不难。其他方式只会让事情变得更糟。它们只是初学者难以阅读,因为他们中的大多数人对此并不熟悉。只需遵循关于代码中元素排序的标准约定。例如。不要将静态初始化器放在代码的中途或整个底部,也不要将它们中的多个散布在类中。只需将一个放在顶部,在静态声明之后。

【讨论】:

  • 这是一个非常有效的观点(所以尽管不接受,但我还是投了赞成票),GPP 也是最好的说明。我不会使用那种方法,因为正如你所说,这只是初学者难以理解/阅读......而且我不能确保在我之后维护它的人有经验:)。
  • 我不确定我是否愿意。通过将其重构为 private static 方法,您可能会失去监督。通常的做法是将这类方法(实用方法)放在类的整个底部。但是好的,现在你有 IDE,所以你可以直接点击。
【解决方案3】:

static 块不难阅读。所以我推荐这个解决方案。 但是,您可以将对象包装在另一个对象中,例如 ObjectNameWrapper 与您的 ObjectName 共享一个 interface,并且其构造函数调用您的 ObjectName 构造函数,隐藏所有发生的已检查异常。但同样,我会选择静态选项。

【讨论】:

  • 引入另一个对象似乎很迟钝。
  • 我当然同意你的看法。您的静态方法建议要好得多。
【解决方案4】:

您可以使用带有 Lombok 的 @SneakyThrows 注释的方法

public static final ObjectName OBJECT_NAME = createObjectName();

@SneakyThrows(SomeException.class)
private static ObjectName createObjectName() {
    return new ObjectName("foo:type=bar");
}

此注解使已检查的异常表现得像未检查的异常。

【讨论】:

    猜你喜欢
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-25
    • 1970-01-01
    相关资源
    最近更新 更多