Java 在私有实用程序类构造函数中,首选的一次性使用是什么?

Java 在私有实用程序类构造函数中,首选的一次性使用是什么?,java,coding-style,throwable,Java,Coding Style,Throwable,,第4项,讨论了使用私有构造函数来强制不可持久性。以下是本书中的代码示例: public final class UtilityClass { private UtilityClass() { throw new AssertionError(); } } 然而,AssertionError似乎不是正确的抛出方式。没有任何东西被“断言”,这就是API如何定义的使用 在这种情况下,是否有一种不同的可丢弃的?人们通常只是抛出一个带有消息的常规异常吗?或者为此编写自定

,第4项,讨论了使用私有构造函数来强制不可持久性。以下是本书中的代码示例:

public final class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}
然而,
AssertionError
似乎不是正确的抛出方式。没有任何东西被“断言”,这就是API如何定义的使用

在这种情况下,是否有一种不同的
可丢弃的
?人们通常只是抛出一个带有消息的常规
异常吗?或者为此编写自定义的
异常
很常见吗


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

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

中断的断言意味着您已经中断了代码的契约规范。所以这是正确的


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

关于什么?:)

不,不,恕我直言,不要抛出
AssertionError
,除非它来自断言。如果您想在这里使用AssertionError,请将其与
assert(false)
一起抛出。然后,阅读代码的人可以稍后找到它

更好的方法是定义自己的异常,比如
cantin实例化eutilityclass
。然后你会有代码,上面写着

try {
    // some stuff
} catch (CantInstantiateUtilityClass e) {
    // react
}
让《捕手》的读者知道发生了什么



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

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

我喜欢包括Bloch的评论:

// Suppress default constructor for noninstantiability
或者更好地将其放入错误中:

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

当代码需要包含JUnit作为依赖项时,例如在maven测试范围内
test
,然后直接转到
Assertion.fail()
方法,并从清晰性的显著改进中获益

public final class UtilityClass {
    private UtilityClass() {
        fail("The UtilityClass methods should be accessed statically");
    }
}
当超出测试范围时,您可以使用如下内容,这需要静态导入才能使用上述内容<代码>导入静态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";
    }
}
内置异常之一UnsupportdOperationException可以被抛出到 指示“不支持请求的操作”

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

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

class NoninstantiabilityError extends Throwable
这有以下优点:

  • 该名称表示问题所在
  • 因为它直接扩展了可丢弃的
,所以它不太可能被意外捕获
  • 因为它直接扩展了Throwable
  • ,所以会检查它,并且意外地调用相应的构造函数需要捕获异常 用法示例:

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

    我的理解是,这个实用程序类只提供了一些静态方法,因此不会调用构造函数。Matthew是正确的。从技术上讲,甚至不需要抛出任何东西,因为构造函数是私有的。抛出一些东西可以确保类本身不会调用构造函数。我不确定我是否喜欢这个,因为它是不兼容的ClassChangeError的子类。但听起来不错。:)“通常,编译器会捕获此错误;只有在类的定义发生不兼容的更改时,才会在运行时发生此错误。”谢谢。我想我只是以错误的方式看待断言。这里强烈反对,见下文。您需要能够找到异常开始的地方;您还应该让异常给您或代码的天真读者一个发生了什么的好提示。与断言不相关的断言错误也不存在。不正确,请参见javadoc:@Charlie javadoc说“抛出以指示断言失败”。您将断言视为“使用断言关键字”,而不是“程序员认为这是一个bug”。您是对的,我是。这就是为什么它被称为“断言”语句。就像我不会使用NullPointerException来表示值为0而不是范围内的数字的名称一样,即使该数字是作为索引的。我强烈反对。(顺便说一下,Bloch,不是Block。)“assert false”的问题是,如果您关闭了断言,那么断言将无法抛出。人们应该通过查找AssertionError和assert来查找断言。-1异常将被某些(可能是随机的)catch(异常e)块捕获。不会出现错误。为只有通过修改源代码或使用自定义类加载器才能发生的事情创建自定义异常是一个糟糕的建议。我不同意,如果调用构造函数,程序应该放弃并死亡,因为这显然是一个bug。assert(false)也会导致程序放弃并死亡,同样带有AssertionError,因此我不确定您为什么喜欢assert关键字。我相信,当您的飞机控制系统放弃并死亡而不是试图恢复时,您一定会激动不已。异常终止不是一种可靠性策略。关于您的更新:实际文本被抛出以表明断言失败。
    它没有说明我们讨论的是谁的断言-至少我有关于我的代码的断言,您可能没有。。但无论如何,整个事情都是吹毛求疵——没有人会有好的理由(“可能不会实例化类X的实例”)和指向实际构造函数的堆栈跟踪来错误解释这样的异常