乐趣区

第4项:通过私有构造器强化不可实例化的能力

  有时候你会想要编写一个只包含一组静态方法和静态字段的类。这种类名声很不好,因为有些人为了避免使用面向对象的思维方式而滥用这样的类 (some people abuse them to avoid thinking in terms of objects),但是他们确实有它们特有的用处。我们可以使用这种类,以 java.lang.Math 或者 java.util.Arrays 的方式对原始值或数组的相关方法组织起来。它们还可以用于以 java.util.Collections 的方式,把实现特定接口的对象上的静态方法(包括工厂:第 1 项) 组织起来。(从 Java 8 开始,你也可以将这些方法放在接口中,假设它是你自己修改的)。最后,这些类可以用于对 final 类的方法组织起来,通过这种方式用以取代扩展该类的做法。
  这种工具类不希望被实例化,实例化对它没有任何意义。然而,在没有显式构造函数的情况下,编译器会默认提供一个公共的、无参的默认构造函数。对于用户而言,这个构造器与其他构造器没有任何区别。在已发行的 API 中常常可以看到一些被无意识地实例化的类。
  企图通过将该类做成抽象类来强制该类不可被实例化,这是行不通的。该类可以被子类化,并且该子类也可以被实例化。这样做甚至会误导用户,以为这种类是专门为了继承而设计的(第 19 项)。然而,有一些简单的习惯用法可以确保类不可被实例化。由于只有当类不包含显示的构造器时,编译器才会生成缺省的构造器,因此我们只要让这个类包含私有构造器,他就不能被实例化了:
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass(({
throw new AssertionError();
}
… // Remainder omitted
}
  因为显示构造函数是私有的,所以它在类外是不可访问的,AssertionError 不是必需的,但是它可以避免不小心在类的内部调用构造器。它保证该类在任何情况下都不会实例化。这种习惯用法有点违背直觉,好像构造器就是专门设计成不能被调用一样。因此明智的做法就是在代码中增加一条注释,如上所示。
  这种习惯用法也有副作用,它使得一个类不能拥有子类。因为子类的所有构造函数都必须显示或者隐式地调用父类的构造函数,在这种情形下,子类就没有可访问的父类构造器可用了。

退出移动版