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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在私有实用程序类构造函数中使用的首选Throwable是什么?相关的知识,希望对你有一定的参考价值。

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

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

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

在这种情况下,是否存在不同的Throwable?人们通常只是向一般的Exception留言吗?或者为此写一个自定义Exception是常见的吗?

这是非常微不足道的,但我认为从形式和标准的角度来看,我只是对它感到好奇。

答案

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

另一答案

我喜欢包括Bloch的评论:

// Suppress default constructor for noninstantiability

或者更好,但把它放在错误中:

private UtilityClass()
{
    throw new AssertionError("Suppress default constructor for noninstantiability");
}
另一答案

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

另一答案

怎么样IllegalAcessError? :)

另一答案

不,不,对Josh Bloch的所有应有的尊重,除非是来自断言,否则永远不要抛出AssertionError。如果你想在这里使用AssertionError,请使用assert(false)抛出它。那么阅读代码的人可以在以后找到它。

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

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

这样捕手的读者才知道发生了什么。

更新

每隔一段时间,一些该死的傻瓜就会在这里徘徊,并在事实发生近四年之后再次投票。所以,我要注意的是,标准仍然将AssertionError定义为断言失败的结果,而不是某些初学者认为应该抛出一个明确定义的信息异常。遗憾的是,良好的异常规则可能是Java编程中最不受鼓励的技能。

另一答案

当代码要求将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.");
}
另一答案

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

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

另一答案

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

class NoninstantiabilityError extends Throwable

这具有以下优点:

  • 名称表示问题
  • 因为它直接扩展Throwable它不太可能被意外捕获
  • 因为它直接扩展Throwable它被检查并且意外地调用相应的构造函数将需要捕获异常

用法示例:

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

    ...
}

以上是关于在私有实用程序类构造函数中使用的首选Throwable是什么?的主要内容,如果未能解决你的问题,请参考以下文章

抽象类中的私有构造函数

为啥私有构造函数在案例类中仍然可见?

Java - SonarQube,关于“实用程序类不应有公共构造函数”(squid:S1118)的问题

指向具有私有构造函数的类的类成员的指针

在 C# 中构造对象的首选方法是啥?构造函数参数或属性?

具有私有构造函数和析构函数的类对象的向量?