Effective Java 第三版读书笔记——条款16:在公共类中使用访问方法而不是公共属性
有时候,你可能会试图写一些退化的类(degenerate classes),除了集中实例属性之外别无用处:
// Degenerate classes like this should not be public!
class Point {
public double x;
public double y;
}
因为这些类的数据属性可以直接被访问,因此这些类不提供封装的好处(条目 15)。在不更改 API 的情况下你无法更改其表示形式,无法强制执行不变量,并且在访问属性时无法执行辅助操作。坚持面向对象的程序员觉得这样的类是很糟糕的,应该被具有私有属性和公共访问方法(getter)的类所取代,而对于可变类来说,它们应该被替换为设值方法(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 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
}