Effective-Java-4通过私有构造器强化不可实例化的能力
***你可能需要编写只包含静态方法和静态域的类(例如一些工具类) 。***这些类的名声很不好,因为有些人在面向对象的语言中滥用这样的类来编写过程化的程序。但它们也的确有他们的用处。[1]
这样的工具类 (utility class) 不希望被实例化,实例对它们没有意义。然而,在缺少显式构造器的情况下,编译器会自动提供一个共有的,无参的缺省构造器(default constructor)。对于用户而言,这个构造器与其他的构造器没有任何的区别,在已发行的API中常常可以看到一些被无意识地实例化的类。[2]
***企图通过将类做成抽象类来强制该类不可被实例化,这样是行不通的。***该类可以被子类化,并且该子类可被实例化。这样甚至会误导用户,以为该类是特意为了继承而设计的。然而,有一些简单的习惯用法可以保证类不被实例化。由于只有当类不包含显式构造器时,编译器才会产生缺省的构造器,因此我们只要让这个类包含私有构造器,它就不能被实例化了:[3]
1 | // Noninstantiable utility class |
由于显示的构造器是私有的,所以不可以在该类的外部访问它。AssertionError不是必需的,但是它可以避免不小心在该类的内部调用构造器。他保证该类在任何情况下都不会被实例化。这种习惯用法有点违背直觉,好像构造器的声明就是设计成不能被调用一样。因此,较好的做法是,在代码中增加注释(见上文)。[4]
这种习惯用法也有其副作用,它使得一个类不能被子类化。所有的构造器都必须显示或隐式地调用超类 (superclass) 构造器,在这种情况下,子类就没有可访问的超累构造器可以调用了。[5]
Joshua Bloch, Effective Java, 3rd Edition, Item 4, discusses noninstantiable utility classes; Oracle Java SE 21 API pages such as Math and Collections are standard-library examples centered on static utility operations. ↩︎
Oracle, Java Language Specification §8.8.9 Default Constructor, specifies that a class with no constructor declarations receives an implicitly declared default constructor. ↩︎
Oracle, Java Language Specification §8.1.1.1 abstract Classes, defines abstract classes as classes that cannot be directly instantiated, while Item 4 of Effective Java explains why abstract is the wrong signal for utility classes that should not be instantiated or extended. ↩︎
Oracle Java SE 21 API, AssertionError, documents the error type; in this idiom, throwing it is a fail-fast guard for accidental constructor calls, while the private constructor is the mechanism that closes the normal construction path. ↩︎
Oracle, Java Language Specification §8.8.7.1 Explicit Constructor Invocations, describes constructor chaining to another constructor or superclass constructor; if a superclass exposes no accessible constructor, an external subclass cannot construct it normally. ↩︎