Occasionally,
you may be tempted to write degenerate classes that serve no purpose other than
to group instance fields:
// Degenerate classes like this should not be
public!
class Point {
public double x;
public double y;
}
Because the
data fields of such classes are accessed directly, these classes do not offer
the benefits of encapsulation (Item 13). You
can’t change the representation without changing the API, you can’t enforce
invariants, and you can’t take auxiliary action when a field is accessed.
Hard-line object-oriented programmers feel that such classes are anathema and
should always be replaced by classes with private fields and public accessor methods (getters) and, for mutable classes, mutators (setters):
// 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; }
}
Certainly, the
hard-liners are correct when it comes to public classes: if a class is
accessible outside its package, provide accessor methods, to preserve
the flexibility to change the class’s internal representation.
However, if a class is
package-private or is a private nested class, there is nothing inherently wrong
with exposing its data fields.
Several
classes in the Java platform libraries violate the advice that public classes
should not expose fields directly. Prominent examples include the Point and Dimension
classes
in the java.awt package.
While it’s
never a good idea for a public class to expose fields directly, it is less
harmful if the fields are immutable. You can’t change the representation of
such a class without changing its API, and you can’t take auxiliary actions
when a field is read, but you can enforce invariants. For example, this class
guarantees that each instance represents a valid time:
// 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
}
In summary,
public classes should never expose mutable fields. It is less harmful, though
still questionable, for public classes to expose immutable fields. It is,
however, sometimes desirable for package-private or private nested classes to
expose fields, whether mutable or immutable.
Reference: Effective Java 2nd Edition by Joshua Bloch