关于java:在私有实用程序类构造函数中使用的首选Throwable是什么?

What is the preferred Throwable to use in a private utility class constructor?

有效的Java(第二版),项目4,讨论使用私有构造函数来执行非实例化。这是这本书的代码示例:

1
2
3
4
5
public final class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}

然而,AssertionError似乎不是正确的投掷方式。没有"断言",这就是API如何定义断言错误的使用。

在这种情况下,是否有一个不同的Throwable?人们通常只是用一条信息抛出一个通用的Exception吗?或者为这个写一个自定义的Exception是常见的吗?

这是非常琐碎的,但比任何事情都重要,我想我只是从风格和标准的角度对它好奇而已。


有一个断言:"我断言永远不会调用这个构造函数"。所以,实际上,AssertionError在这里是正确的。


我喜欢包括布洛赫的评论:

1
// Suppress default constructor for noninstantiability

或者更好的方法是将其放入错误中:

1
2
3
4
private UtilityClass()
{
    throw new AssertionError("Suppress default constructor for noninstantiability");
}


UnsupportedOperationException听起来是最合适的,尽管选中的异常会更好,因为它可能会警告某人在编译时错误地实例化类。


不,不,不,恕我直言,除非是来自断言,否则永远不要扔一个江户。如果你想要一个断言者,把它和assert(false)一起扔。然后读代码的人可以稍后找到它。

更好的是,定义自己的例外,比如CantInstantiateUtilityClass。然后你会得到代码,上面写着

1
2
3
4
5
try {
    // some stuff
} catch (CantInstantiateUtilityClass e) {
    // react
}

让接球手的读者知道发生了什么。

更新

每隔一段时间,就有一个该死的蠢货在这里游荡,然后在事实发生四年后再次投了反对票。所以,我要注意的是,标准仍然将AssertionError定义为一个失败的断言的结果,而不是一些初学者认为应该抛出的东西来代替一个定义明确的信息异常。可悲的是,良好的异常规则可能是Java编程中最不被鼓励的技能。


非法访问错误怎么办?:)


当代码要求将JUnit作为依赖项包括在Maven测试范围test内时,则直接使用Assertion.fail()方法,并受益于清晰度的显著提高。

1
2
3
4
5
public final class UtilityClass {
    private UtilityClass() {
        fail("The UtilityClass methods should be accessed statically");
    }
}

当超出测试范围时,您可以使用类似于以下内容的东西,这需要像上面一样使用静态导入。import static pkg.Error.fail;

1
2
3
4
5
6
7
8
9
public class Error {
    private static final Logger LOG = LoggerFactory.getLogger(Error.class);
    public static void fail(final String message) {
        LOG.error(message);
        throw new AssertionError(message);
        // or use your preferred exception
        // e.g InstantiationException
    }
}

以下用法。

1
2
3
4
5
public class UtilityClassTwo {
    private UtilityClassTwo() {
        Error.fail("The UtilityClass methods should be accessed statically");
    }
}

在最惯用的形式中,它们都归结为:

1
2
3
4
5
public class UtilityClassThree {
    private UtilityClassThree() {
        assert false :"The UtilityClass methods should be accessed statically";
    }
}

内置异常之一,UnsupportedOperationException可以被引发到指示"不支持请求的操作"。

1
2
3
4
 private Constructor() {
    throw new UnsupportedOperationException(
           "Do not instantiate this class, use statically.");
}


您可以创建自己的类扩展Throwable,例如:

1
class NoninstantiabilityError extends Throwable

这有以下优点:

  • 名称表示问题所在
  • 因为它直接延伸到Throwable,所以它不太可能被意外捕获。
  • 因为它直接扩展了Throwable,所以检查并意外调用相应的构造函数需要捕获异常

使用实例:

1
2
3
4
5
6
7
public final class UtilityClass {
    private UtilityClass() throws NoninstantiabilityError {
        throw new NoninstantiabilityError();
    }

    ...
}

断开的断言意味着您已经断开了代码的合同规范。所以这是正确的。

但是,正如我假设您将在私下实例化一个实例,它还将调用构造函数并导致错误-除非您有另一个构造函数?