Java in a Nutshell, 5th Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java in a Nutshell, 5th Edition [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


3.6. Data Hiding and Encapsulation


We started this chapter by
describing a class as a collection of data and methods. One of the
important object-oriented techniques we haven't
discussed so far is hiding the data within the class and making it
available only through the methods. This technique is known as

encapsulation because it seals the data (and
internal methods) safely inside the
"capsule" of the class, where it
can be accessed only by trusted users (i.e., the methods of the
class).

Why would you want to do this? The most important reason is to hide
the internal implementation details of your class. If you prevent
programmers from relying on those details, you can safely modify the
implementation without worrying that you will break existing code
that uses the class.

Another reason for encapsulation is to protect your class against
accidental or willful stupidity. A class often contains a number of
interdependent fields that must be in a consistent state. If you
allow a programmer (including yourself) to manipulate those fields
directly, he may change one field without changing important related
fields, leaving the class in an inconsistent state. If instead he has
to call a method to change the field, that method can be sure to do
everything necessary to keep the state consistent. Similarly, if a
class defines certain methods for internal use only, hiding these
methods prevents users of the class from calling them.

Here's
another way to think about encapsulation: when all the data for a
class is hidden, the



methods define the only
possible operations that can be performed on objects of that class.
Once you have carefully tested and debugged your methods, you can be
confident that the class will work as expected. On the other hand, if
all the fields of the class can be directly manipulated, the number
of possibilities you have to test becomes unmanageable.

Other reasons to hide fields and methods of a class include:

  • Internal fields and methods that are visible outside the class just
    clutter up the API. Keeping visible fields to a minimum keeps your
    class tidy and therefore easier to use and understand.

  • If a field or method is visible to the users of your class, you have
    to document it. Save yourself time and effort by hiding it instead.



3.6.1. Access Control



All
the
fields and methods of a class can always
be used within the body of the class itself. Java defines access
control rules that restrict members of a class from being used
outside the class. In a number of examples in this chapter,
you've seen the public modifier
used in field and method declarations. This
public keyword, along with
protected and private, are

access

control

modifiers ; they
specify the access rules for the field or
method.


3.6.1.1 Access to packages

A
package is always accessible to code defined within the package.
Whether it is accessible to code from other packages depends on the
way the package is deployed on the host system. When the class files
that comprise a package are stored in a directory, for example, a
user must have read access to the directory and the files within it
in order to have access to the package. Package access is not part of
the Java language itself. Access control is usually done at the level
of classes and members of classes instead.


3.6.1.2 Access to classes

By
default, top-level classes are accessible within the package in which
they are defined. However, if a top-level class is declared
public, it is accessible everywhere (or everywhere
that the package itself is accessible). The reason that
we've restricted these statements to top-level
classes is that, as we'll see later in this chapter,
classes can also be defined as members of other classes. Because
these inner classes are members of a class, they obey the member
access-control rules.


3.6.1.3 Access to members

The
members of a class are always accessible within the
body of the class. By default, members
are also accessible throughout the package in which the class is
defined. This implies that classes placed in the same package should
trust each other with their internal implementation details. This
default level of access is often called

package access . It
is only one of four possible levels of access. The other three levels
of access are defined by the public,
protected, and private
modifiers. Here is some example code that uses these modifiers:

public class Laundromat {      // People can use this class. 
private Laundry[] dirty; // They cannot use this internal field,
public void wash() { ... } // but they can use these public methods
public void dry() { ... } // to manipulate the internal field.
protected int temperature; // A subclass might want to tweak this field
}

These access rules apply to members of a class:

  • If a member of a class is declared with the public
    modifier, it means that the member is accessible anywhere the
    containing class is accessible. This is the least restrictive type of
    access control.

  • If a member of a class is declared private, the
    member is never accessible, except within the class itself. This is
    the most restrictive type of access control.

  • If a member of a class is declared protected, it
    is accessible to all classes within the package (the same as the
    default package accessibility) and also accessible within the body of
    any subclass of the class, regardless of the package in which that
    subclass is defined. This is more restrictive than
    public access, but less restrictive than package
    access.

  • If a member of a class is not declared with any of these modifiers,
    it has the default package access: it is accessible to code within
    all classes that are defined in the same package but inaccessible
    outside of the package.


protected access requires a little more
elaboration. Suppose class A declares a
protected field x and is
extended by a class B, which is defined in a
different package (this last point is important). Class
B inherits the protected field
x, and its code can access that field in the
current instance of B or in any other instances of
B that the code can refer to. This does not mean,
however, that the code of class B can start
reading the protected fields of arbitrary instances of
A! If an object is an instance of
A but is not an instance of B,
its fields are obviously not inherited by B, and
the code of class B cannot read them.


3.6.1.4 Access control and inheritance



The Java specification states that a
subclass inherits all the instance fields and instance methods of its
superclass accessible to it. If the subclass is defined in the same
package as the superclass, it inherits all

non-private instance
fields and methods. If the subclass is defined in a different
package, however, it inherits all protected and
public instance fields and methods.
private fields and methods are never inherited;
neither are class fields or class methods. Finally, constructors are
not inherited; they are chained, as described earlier in this
chapter.

The statement that a subclass does not inherit the inaccessible
fields and methods of its superclass can be a confusing one. It would
seem to imply that when you create an instance of a subclass, no
memory is allocated for any private fields defined
by the superclass. This is not the intent of the statement, however.
Every instance of a subclass does, in fact, include a complete
instance of the superclass within it, including all inaccessible
fields and methods. It is simply a matter of terminology. Because the
inaccessible fields cannot be used in the subclass, we say they are
not inherited. Earlier in this section we said that the members of a
class are always accessible within the body of the class. If this
statement is to apply to all members of the class, including
inherited members, we must define "inherited
members" to include only those members that are
accessible. If you don't care for this definition,
you can think of it this way instead:

  • A class inherits

    all instance fields and
    instance methods (but not constructors) of its superclass.

  • The body of a class can always access all
    the fields and methods it declares itself. It can also access the

    accessible fields and members it inherits from
    its superclass.



3.6.1.5 Member access summary

Table 3-1 summarizes the member access
rules.

Table 3-1. Class member accessibility

Member visibility


Accessible to


Public


Protected


Package


Private


Defining class


Yes


Yes


Yes


Yes


Class in same package


Yes


Yes


Yes


No


Subclass in different package


Yes


Yes


No


No


Non-subclass different package


Yes


No


No


No

Here are some simple rules of thumb for using
visibility
modifiers:

  • Use public only for
    methods and constants that form part of the public API of the class.
    Certain important or frequently used fields can also be
    public, but it is common practice to make fields
    non-public and encapsulate them with
    public accessor methods.

  • Use protected for
    fields and methods that aren't required by most
    programmers using the class but that may be of interest to anyone
    creating a subclass as part of a different package. Note that
    protected members are technically part of the
    exported API of a class. They should be documented and cannot be
    changed without potentially breaking code that relies on them.

  • Use the default package
    visibility for fields and methods that are internal implementation
    details but are used by cooperating classes in the same package. You
    cannot take real advantage of package visibility unless you use the
    package directive to group your cooperating
    classes into a package.

  • Use private for
    fields and methods that are used only inside the class and should be
    hidden everywhere else.


If you are not sure whether to use protected,
package, or private accessibility, it is better to
start with overly restrictive member access. You can always relax the
access restrictions in future versions of your class, if necessary.
Doing the reverse is not a good idea because increasing access
restrictions is not a backward-compatible change and can break code
that relies on access to those members.


3.6.2. Data Accessor Methods


In the Circle example,
we declared the circle radius to be a public
field. The Circle class is one in which it may
well be reasonable to keep that field publicly accessible; it is a
simple enough class, with no dependencies between its fields. On the
other hand, our current implementation of the class allows a
Circle object to have a negative radius, and
circles with negative radii should simply not exist. As long as the
radius is stored in a public field, however, any
programmer can set the field to any value she wants, no matter how
unreasonable. The only solution is to restrict the
programmer's direct access to the field and define
public methods that provide indirect access to the
field. Providing public methods to read and write
a field is not the same as making the field itself
public. The crucial difference is that methods can
perform error checking.

Example 3-4 shows how we might reimplement
Circle to prevent circles with negative radii.
This version of Circle declares the
r field to be protected and
defines accessor methods named getradius( ) and
setRadius() to read and write the field value
while enforcing the restriction on negative radius values. Because
the r field is protected, it is
directly (and more efficiently) accessible to subclasses.


Example 3-4. The Circle class using data hiding and encapsulation

package shapes;           // Specify a package for the class
public class Circle { // The class is still public
// This is a generally useful constant, so we keep it public
public static final double PI = 3.14159;
protected double r; // Radius is hidden but visible to subclasses
// A method to enforce the restriction on the radius
// This is an implementation detail that may be of interest to subclasses
protected void checkRadius(double radius) {
if (radius < 0.0)
throw new IllegalArgumentException("radius may not be negative.");
}
// The constructor method
public Circle(double r) {
checkRadius(r);
this.r = r;
}
// Public data accessor methods
public double getRadius() { return r; }
public void setRadius(double r) {
checkRadius(r);
this.r = r;
}
// Methods to operate on the instance field
public double area() { return PI * r * r; }
public double circumference() { return 2 * PI * r; }
}

We have defined the Circle class within a package
named shapes. Since r is
protected, any other classes in the
shapes package have direct access to that field
and can set it however they like. The assumption here is that all
classes within the shapes package were written by
the same author or a closely cooperating group of authors and that
the classes all trust each other not to abuse their privileged level
of access to each other's implementation details.

Finally, the code that enforces the restriction against negative
radius values is itself placed within a protected
method, checkRadius(). Although users of the
Circle class cannot call this method, subclasses
of the class can call it and even override it if they want to change
the restrictions on the radius.

Note particularly the
getradius() and setRadius( )
methods of Example 3-4. It is a common convention in
Java that data accessor methods begin with the prefixes
" get" and
"set." If the field being accessed
is of type boolean, however, the
get() method may be replaced with an equivalent
method that begins with "is." For
example, the accessor method for a boolean field
named readable is typically called
isReadable( ) instead of
getreadable(). In the programming conventions of
the JavaBeans component model (covered in Chapter 7), a hidden field with one or more data
accessor methods whose names begin with
"get,"
"is," or
"set" is called a

property .
An interesting way to study a complex class is to look at the set of
properties it defines. Properties are particularly common in the AWT
and Swing APIs, which are covered in

Java Foundation Classes in
a Nutshell (O'Reilly).


/ 1191