A well-designed
module hides all of its implementation details, cleanly separating its API from
its implementation. Modules then communicate only through their APIs and are
oblivious to each others’ inner workings. This concept, known as information hiding or encapsulation, is one of
the fundamental tenets of software design.
Information
hiding is important for many reasons, most of which stem from the fact that it decouples the modules that comprise a system, allowing them to be developed,
tested, optimized, used, understood, and modified in isolation. This speeds up
system development because modules can be developed in parallel. It eases the
burden of maintenance because modules can be understood more quickly and
debugged with little fear of harming other modules.
Finally,
information hiding decreases the risk in building large systems, because
individual modules may prove successful even if the system does not.
Java has many
facilities to aid in information hiding. The access
control mechanism
[JLS, 6.6] specifies the accessibility of classes,
interfaces, and members.
The rule of
thumb is simple: make each class or member as inaccessible as possible. In other
words, use the lowest possible access level consistent with the proper
functioning of the software that you are writing.
For top-level
(non-nested) classes and interfaces, there are only two possible access levels:
package-private and public. If you declare a top-level class or interface with the public modifier, it will be public; otherwise, it
will be package-private. If a top-level class or interface can be made
package-private, it should be. By making it package-private, you make it part
of the implementation rather than the exported API, and you can modify it,
replace it, or eliminate it in a subsequent release without fear of harming
existing clients. If you make it public, you are obligated to support it
forever to maintain compatibility.
For members
(fields, methods, nested classes, and nested interfaces), there are four
possible access levels, listed here in order of increasing accessibility:
• private—The member is
accessible only from the top-level class where it is declared.
• package-private—The member is
accessible from any class in the package where it is declared. Technically
known as default access, this is the access level
you get if no access modifier is specified.
• protected—The member is
accessible from subclasses of the class where it is declared and from any class
in the package where it is declared.
• public—The member is
accessible from anywhere.
After
carefully designing your class’s public API, your reflex should be to make all
other members private. Only if another class in the same package really needs
to access a member should you remove the private
modifier,
making the member package-private.
Instance fields
should never be public (Item 14). If an instance field is nonfinal,
or is a final reference to a mutable object, then by making the field public,
you give up the ability to limit the values that can be stored in the field.
Classes with
public mutable fields are not thread-safe. Even if a field is final and
refers to an immutable object, by making the field public you give up the
flexibility to switch to a new internal data representation in which the field
does not exist.
Note that a
nonzero-length array is always mutable, so it is wrong for a class to have a
public static final array field, or an accessor that returns such a field. If a class
has such a field or accessor, clients will be able to modify the contents of
the array. This is a frequent source of security holes:
// Potential security hole!
public static final Thing[] VALUES = { ... };
Beware of the
fact that many IDEs generate accessors that return references to private array
fields, resulting in exactly this problem. There are two ways to fix the
problem. You can make the public array private and add a public immutable list:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
Alternatively,
you can make the array private and add a public method that returns a copy of a
private array:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
To choose
between these alternatives, think about what the client is likely to do with
the result. Which return type will be more convenient? Which will give better
performance?
To summarize,
you should always reduce accessibility as much as possible. After carefully
designing a minimal public API, you should prevent any stray classes,
interfaces, or members from becoming a part of the API. With the exception of
public static final fields, public classes should have no public fields. Ensure
that objects referenced by public static final fields are immutable.
Reference: Effective Java 2nd Edition by Joshua Bloch