ITEM 16: IN PUBLIC CLASSES, USE ACCESSOR METHODS, NOT PUBLIC FIELDS
偶尔,您可能会忍不住编写一些退化类,它们除了对实例字段进行分组外,没有其他用途:
class Point {
public double x;
public double y;
}
因为能直接访问这些类的数据字段,所以这些类不能提供封装的好处(item 15)。您不能在不更改API的情况下更改表示形式,不能强制执行不变量,也不能在访问字段时采取辅助操作。强硬的面向对象程序员认为这样的类是可恶的,应该总是用带有 private 字段和 public 访问方法(getter)的类替换,对于可变类,应该使用mutators (setter):
// Encapsulation of data by accessor methods and mutators
class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() { return x; }
public double getY() { return y; }
public void setX(double x) { this.x = x; }
public void setY(double y) { this.y = y; }
}
当然,当涉及到 public 类时,强硬派是正确的:如果一个类在它的包之外是可访问的,那么提供访问器方法来保持更改类内部表示的灵活性。如果一个 public 类公开了它的数据字段,那么更改它的表示形式的所有希望都将落空,因为客户机代码可以分布得非常广泛。
然而,如果一个类是package-private 的或者是 private 嵌套类,那么公开它的数据字段并没有本质上的错误——假设它们在描述类提供的抽象方面做得足够好。无论在类定义中,还是在使用类定义的客户机代码中,这种方法都比访问器方法生成更少的视觉混乱。虽然客户机代码绑定到类的内部表示,但此代码仅限于包含该类的包。如果需要更改表示形式,则可以在不触及包外部任何代码的情况下进行更改。对于 private 嵌套类,更改的范围进一步限制为封闭类。
Java平台库中的几个类违反了 public 类不应该直接公开字段的建议。典型的例子包括 java.awt 包中的 Point 和 Dimension类。这些类应该被视为警示反例,而不是要模仿的例子。如 item 67 所述,Dimension 类的内部结构的决定导致了一个严重的性能问题,这个问题今天仍然存在。
虽然 public 类直接公开字段从来都不是一个好主意,但是如果字段是不可变的,那么危害就会小一些。如果不更改该类的API,就不能更改该类的表示形式,并且在读取字段时不能采取辅助操作,但是可以强制执行不变量。例如,这个类保证每个实例都表示一个有效的时间:
// Public class with exposed immutable fields - questionable
public final class Time {
private static final int HOURS_PER_DAY = 24;
private static final int MINUTES_PER_HOUR = 60;
public final int hour;
public final int minute;
public Time(int hour, int minute) {
if (hour < 0 || hour >= HOURS_PER_DAY)
throw new IllegalArgumentException("Hour: " + hour);
if (minute < 0 || minute >= MINUTES_PER_HOUR)
throw new IllegalArgumentException("Min: " + minute);
this.hour = hour;
this.minute = minute;
}
...
// Remainder omitted
}
总之,public 类永远不应该公开可变字段。对于 public 类来说,公开不可变字段的危害要小一些,尽管仍然存在疑问。但是,有时需要 package-private 或 private 嵌套类公开字段,无论是可变的还是不可变的。
网友评论