34

Effective Java (Second Edition)的第 4 条讨论了使用私有构造函数来强制执行不可实例化。这是书中的代码示例:

public final class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}

然而,AssertionError这似乎不是正确的扔东西。没有任何东西被“断言”,这就是 API 定义AssertionError使用的方式。

Throwable在这种情况下通常有什么不同吗?一个人通常只是抛出一个Exception带有信息的将军吗?或者为此编写一个习惯是否很常见Exception

这很简单,但我想我只是从风格和标准的角度对它感到好奇。

4

8 回答 8

40

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

于 2008-12-29T22:46:27.483 回答
11

我喜欢包括 Bloch 的评论:

// Suppress default constructor for noninstantiability

或者更好的是把它放在错误中:

private UtilityClass()
{
    throw new AssertionError("Suppress default constructor for noninstantiability");
}
于 2008-12-30T17:26:29.250 回答
5

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

于 2008-12-29T23:43:20.847 回答
2

那么IllegalAcessError呢?:)

于 2008-12-29T22:50:04.167 回答
2

不不不,非常尊重 Josh Bloch,除非它来自断言,否则永远不要抛出 an 。AssertionError 如果你想要一个 AssertionError 这里,用assert(false). 然后阅读代码的人可以稍后找到它。

更好的是,定义你自己的异常,比如说CantInstantiateUtilityClass。然后你会有代码说

try {
    // some stuff
} catch (CantInstantiateUtilityClass e) {
    // react
}

以便捕手的读者知道发生了什么


让我注意,标准仍然定义AssertionError为断言失败的结果,而不是一些初学者认为应该抛出的东西来代替定义明确的信息异常。可悲的是,良好的异常规则可能是 Java 编程中最不鼓励的技能。

于 2008-12-29T23:30:02.747 回答
2

当代码需要包含 JUnit 作为依赖项时,例如在 maven 测试范围内<scope>test</scope>,那么直接进入Assertion.fail()方法并从清晰度的显着改进中受益。

public final class UtilityClass {
    private UtilityClass() {
        fail("The UtilityClass methods should be accessed statically");
    }
}

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

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
    }
}

其中以下用法。

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

以其最惯用的形式,它们都归结为:

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

可以抛出内置异常之一 UnsupportedOperationException 以指示“不支持请求的操作”。

 private Constructor() {
    throw new UnsupportedOperationException(
            "Do not instantiate this class, use statically.");
}
于 2018-07-15T14:15:57.783 回答
0

断断续续的断言意味着您破坏了代码的合同规范。所以这是正确的事情。

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

于 2008-12-29T22:48:00.507 回答
0

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

class NoninstantiabilityError extends Throwable

这具有以下优点:

  • 名字就说明问题
  • 因为它直接延伸Throwable所以不太可能被意外抓住
  • 因为它直接扩展Throwable它被检查并且意外调用相应的构造函数需要捕获异常

使用示例:

public final class UtilityClass {
    private UtilityClass() throws NoninstantiabilityError {
        throw new NoninstantiabilityError();
    }

    ...
}
于 2019-01-24T19:54:10.400 回答