本文是 《Effective Java 3》第四章《类和接口》的学习笔记:在公共类中,使用访问器方法,而不是公共字段。

介绍

与其直接将类的内部字段公开为公共字段,推荐使用访问器方法(也称为 getter 和 setter)来访问和修改对象的状态。这样可以使类对其内部表示保持控制,并为类的客户端提供一种抽象程度。

通过使用访问器方法,您可以实现以下目标:

  1. 封装内部表示(Encapsulate Internal Representation):
    • Getter 方法:Getter 方法用于获取私有字段的值。通过使用 getter 方法,可以将字段的访问限制在类的内部,从而隐藏了字段的具体实现细节。客户端只能通过调用 getter 方法来获取字段的值,而无法直接访问字段本身。
    • Setter 方法:Setter 方法用于设置私有字段的值。使用 setter 方法,可以对字段进行验证、约束和逻辑处理。这样,类可以对字段的修改进行控制,并确保只有经过验证的值才能被设置。
  2. 控制访问和修改(Control Access and Modification):
    • Getter 方法:通过 getter 方法,可以对字段的访问进行控制。例如,可以在 getter 方法中添加权限检查,只允许特定的用户或角色获取字段的值。还可以在 getter 方法中进行计算或转换,以便返回不同于字段本身的值。
    • Setter 方法:Setter 方法允许对字段的修改进行控制。在 setter 方法中,可以进行输入验证、范围检查和其他逻辑处理。这样可以确保只有符合规定的值才能被设置到字段中,从而保持类的状态的一致性和完整性。
  3. 促进演化和兼容性:如果使用公共字段,并且以后需要更改表示方式或添加附加逻辑,则很难保持向后兼容性。然而,通过使用访问器方法,可以修改内部表示或添加新行为,而不会影响类的客户端。

反例

Java 库中的几个类违反了公共类不应该直接公开字段的建议。突出的例子包括 java.awt 包中的 Point 和 Dimension。

在 Java 的早期版本中,一些类设计并没有遵循现代的面向对象设计原则和最佳实践。这些类中的字段被直接声明为公共(public),而没有提供相应的访问器方法。

例如,java.awt 包中的 Point 类和 Dimension 类提供了公共的 x、y 和 width、height 字段来表示点的坐标和矩形的宽度和高度。这意味着客户端代码可以直接访问和修改这些字段,绕过了封装和控制的机制。

这种设计方式存在一些问题:

  1. 缺乏封装:直接公开字段破坏了封装的原则,使得类的内部表示暴露给外部,导致了不可预测的行为和潜在的错误。
  2. 限制扩展性:如果需要在这些类中添加验证逻辑、计算属性或实现其他行为,会面临困难,因为不能在字段被直接访问的情况下进行控制和修改。

除了 java.awt 包中的 Point 和 Dimension 类之外,还有其他一些 Java 库中的类违反了"公共类不应该直接公开字段"的建议。以下是一些例子:

  1. java.util 包中的 Date 类:在早期版本的 Java 中,Date 类的字段(如年、月、日、小时等)是公共的,可以直接访问和修改。这种设计导致了 Date 类的可变性和线程安全性问题。随后,Java 引入了新的日期和时间 API(java.time 包),其中封装了更好的设计原则,遵循了使用访问器方法的建议。
  2. java.util 包中的 Vector 类:Vector 类是一个动态数组,它在早期版本中使用了公共字段来表示元素数量(elementCount)和容量(capacity)。这种设计违反了封装性和控制访问的原则。随着 Java 集合框架的发展,推荐使用 ArrayList 等更现代的集合类,它们使用私有字段并提供了相应的访问器方法。

扩展

如何确保字段的可见性限定为包级私有?

在一些特定的情况下,对于包级私有或私有嵌套类,有时候需要公开字段,无论这个类是可变的还是不可变的。以下是这种情况的一些例子:

  1. 不可变类的常量字段:对于不可变类(Immutable Class),其中的字段一旦被初始化就不会再改变。在这种情况下,将字段声明为公共(public)且不可变的常量是可以接受的。这样可以提供方便的访问和使用方式。例如,java.lang 包中的 String 类就有一个公共的常量字段:public static final String EMPTY = "";
  2. 公共常量字段:有些类可能包含一些公共的常量字段,这些字段在整个包或模块中都是可见的。这样的字段通常是不可变的,并且在设计中被视为公共的 API 的一部分。例如,java.awt 包中的 Color 类具有一些预定义的公共常量字段,如 RED、GREEN、BLUE 等。

需要注意的是,这些情况是例外而不是常规规则。在一般情况下,尽量避免直接公开字段,并使用访问器方法(getter 和 setter)来访问和修改类的状态。这样可以提供更好的封装性和控制访问的能力,同时保护类的内部表示和不变性。

在编写和设计代码时,应该根据具体情况来判断是否需要公开字段。如果确定有必要公开字段,确保字段的可见性限定为包级私有(package-private),以限制对字段的访问范围,并遵循最小暴露原则。