One of the most important aspects of object-oriented design is data hiding, or encapsulation. By treating an object in some respects as a “black box” and ignoring the details of its implementation, we can write stronger, simpler code with components that can be easily reused.
By default, the variables and methods of a class are accessible to members of the class itself and to other classes in the same package. To borrow from C++ terminology, classes in the same package are friendly . We’ll call this the default level of visibility. As you’ll see as we go on, the default visibility lies in the middle of the range of restrictiveness that can be specified.
The modifiers public
and private
, on
the other hand, define the extremes. As we mentioned earlier, methods
and variables declared as private
are accessible
only within their class. At the other end of the spectrum, members
declared as public
are accessible from any class
in any package, provided the class itself can be seen. (The class
that contains the methods must be public
to be
seen outside of its package, as we discussed previously.) The
public
members of a class should define its most
general functionality—what the black box is supposed to do.
Figure 6.7 illustrates the four simplest
levels of visibility, continuing the example from the previous
section. Public members in TextArea
are accessible
from anywhere. Private members are not visible from outside the
class. The default visibility allows access by
other classes in the package.
The protected
modifier allows special access
permissions for subclasses. Contrary to how it might sound,
protected
is slightly less restrictive than the
default level of accessibility. In addition to the default access
afforded classes in the same package, protected
members are visible to subclasses of the class, even if they are
defined in a different package. If you are a C++ programmer and so
are used to more restrictive meanings, this may rub you the wrong
way. [21]
Table 6.1 summarizes the levels of visibility available in Java; it runs generally from most restrictive to least. Methods and variables are always visible within a class, so the table doesn’t address those.
Subclasses add two important
(but unrelated) complications to the topic of visibility. First, when
you
override methods in a subclass, the
overriding method must be at least as visible as the overridden
method. While it is possible to take a private
method and override it with a public
method in a
subclass, the reverse is not possible; you can’t override a
public
method with a private
method. This restriction makes sense if you realize that subtypes
have to be usable as instances of their supertype (e.g., a
Mammal
is a subclass of Animal
and therefore must be usable as an Animal
). If we
could override a method with a less visible method, we would have a
problem: our Mammal
might not be able to do all
the things an Animal
can. However, we can reduce
the visibility of a variable. In this case, the variable acts like
any other shadowed variable; the two variables are distinct and can
have separate visibilities in different classes.
The next complication is a bit harder to follow: the
protected
variables of a class are visible to its
subclasses, but only through objects of the subclass’s type or
its subtypes. In other words, a subclass can see a
protected
variable of its superclass as an
inherited variable, but it can’t access that same variable in a
separate instance of the superclass itself. This can be confusing,
because we often forget that visibility modifiers don’t
restrict access between instances of the same class in the same way
that they restrict access between instances of different classes. Two
instances of the same type of object can normally access all of each
other’s members, including private ones. Said another way: two
instances of Cat
can access all of each
other’s variables and methods (including private ones), but a
Cat
can’t access a protected member in an
instance of Animal
unless the compiler can prove
that the Animal
is a Cat
. If
you found this hard to follow, don’t worry too much. You
shouldn’t run into these issues very often.
Interfaces behave like
classes within packages. An interface can
be declared public
to make it visible outside of
its package. Under the default visibility, an interface is visible
only inside of its package. There can be only one
public
interface declared in a
compilation unit.
[21] Early on, the Java language allowed for certain
combinations of modifiers, one of which was
privateprotected
. The meaning of private protected
was to limit visibility strictly to subclasses (and remove package
access). This was later deemed confusing and overly complex. It is no
longer supported.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.