System, but they may not be reproduced for publication



Yüklə 83 Mb.
Pdf görüntüsü
səhifə35/82
tarix19.04.2023
ölçüsü83 Mb.
#106251
1   ...   31   32   33   34   35   36   37   38   ...   82
Java A Beginner’s Guide, Eighth Edition ( PDFDrive )

 Chapter 6 Self Test
1.
Given this fragment,
is the following fragment correct?
2.
An access modifier must __________ a member’s declaration.
3.
The complement of a queue is a stack. It uses first­in, last­out accessing and is often
likened to a stack of plates. The first plate put on the table is the last plate used. Create
a stack class called Stack that can hold characters. Call the methods that access the
stack push( ) and pop( ). Allow the user to specify the size of the stack when it is
created. Keep all other members of the Stack class private. (Hint: You can use the
Queue class as a model; just change the way the data is accessed.)
4.
Given this class,
write a method called swap( ) that exchanges the contents of the objects referred to by


two Test object references.
5.
Is the following fragment correct?
6.
Write a recursive method that displays the contents of a string backwards.
7.
If all objects of a class need to share the same variable, how must you declare that
variable?
8.
Why might you need to use a static block?
9.
What is an inner class?
10.
To make a member accessible by only other members of its class, what access
modifier must be used?
11.
The name of a method plus its parameter list constitutes the method’s
_______________.
12.
An int argument is passed to a method by using call­by­_______________.
13.
Create a varargs method called sum( ) that sums the int values passed to it. Have
it return the result. Demonstrate its use.
14.
Can a varargs method be overloaded?
15.
Show an example of an overloaded varargs method that is ambiguous.


Chapter 7
Inheritance
Key Skills & Concepts

Understand inheritance basics

Call superclass constructors

Use super to access superclass members

Create a multilevel class hierarchy

Know when constructors are called
History
Topics
Tutorials
Offers & Deals
Highlights
Settings
Support
Sign Out


I

Understand superclass references to subclass objects

Override methods

Use overridden methods to achieve dynamic method dispatch

Use abstract classes

Use final

Know the Object class
nheritance is one of the three foundation principles of object­oriented programming
because it allows the creation of hierarchical classifications. Using inheritance, you can
create a general class that defines traits common to a set of related items. This class can
then be inherited by other, more specific classes, each adding those things that are
unique to it.
In the language of Java, a class that is inherited is called a superclass. The class that
does the inheriting is called a subclass. Therefore, a subclass is a specialized version of
a superclass. It inherits all of the variables and methods defined by the superclass and
adds its own, unique elements.
INHERITANCE BASICS
Java supports inheritance by allowing one class to incorporate another class into its
declaration. This is done by using the extends keyword. Thus, the subclass adds to
(extends) the superclass.
Let’s begin with a short example that illustrates several of the key features of
inheritance. The following program creates a superclass called TwoDShape, which
stores the width and height of a two­dimensional object, and a subclass called
Triangle. Notice how the keyword extends is used to create a subclass.



The output from this program is shown here:
Here, TwoDShape defines the attributes of a “generic” two­dimensional shape, such
as a square, rectangle, triangle, and so on. The Triangle class creates a specific type of
TwoDShape, in this case, a triangle. The Triangle class includes all of TwoDObject
and adds the field style, the method area( ), and the method showStyle( ). The
triangle’s style is stored in style. This can be any string that describes the triangle, such
as "filled", "outlined", "transparent", or even something like "warning symbol",
"isosceles", or "rounded". The area( ) method computes and returns the area of the
triangle, and showStyle( ) displays the triangle style.
Because Triangle includes all of the members of its superclass, TwoDShape, it can
access width and height inside area( ). Also, inside main( ), objects t1 and t2 can
refer to width and height directly, as if they were part of Triangle. 
Figure 7­1
 depicts
conceptually how TwoDShape is incorporated into Triangle.
Even though TwoDShape is a superclass for Triangle, it is also a completely
independent, stand­alone class. Being a superclass for a subclass does not mean that
the superclass cannot be used by itself. For example, the following is perfectly valid:
Of course, an object of TwoDShape has no knowledge of or access to any subclasses of
TwoDShape.


Figure 7­1 A conceptual depiction of the Triangle class
The general form of a class declaration that inherits a superclass is shown here:
class subclass­name extends superclass­name {
// body of class
}
You can specify only one superclass for any subclass that you create. Java does not
support the inheritance of multiple superclasses into a single subclass. (This differs
from C++, in which you can inherit multiple base classes. Be aware of this when
converting C++ code to Java.) You can, however, create a hierarchy of inheritance in
which a subclass becomes a superclass of another subclass. Of course, no class can be a
superclass of itself.
A major advantage of inheritance is that once you have created a superclass that defines
the attributes common to a set of objects, it can be used to create any number of more
specific subclasses. Each subclass can precisely tailor its own classification. For
example, here is another subclass of TwoDShape that encapsulates rectangles:
The Rectangle class includes TwoDShape and adds the methods isSquare( ),
which determines if the rectangle is square, and area( ), which computes the area of a


rectangle.
MEMBER ACCESS AND INHERITANCE
As you learned in 
Chapter 6
, often an instance variable of a class will be declared
private to prevent its unauthorized use or tampering. Inheriting a class does not
overrule the private access restriction. Thus, even though a subclass includes all of the
members of its superclass, it cannot access those members of the superclass that have
been declared private. For example, if, as shown here, width and height are made
private in TwoDShape, then Triangle will not be able to access them:
The Triangle class will not compile because the reference to width and height inside
the area( ) method causes an access violation. Since width and height are declared
private, they are accessible only by other members of their own class. Subclasses have
no access to them.


Remember that a class member that has been declared private will remain private to
its class. It is not accessible by any code outside its class, including subclasses.
At first, you might think that the fact that subclasses do not have access to the private
members of superclasses is a serious restriction that would prevent the use of private
members in many situations. However, this is not true. As explained in 
Chapter 6
, Java
programmers typically use accessor methods to provide access to the private members
of a class. Here is a rewrite of the TwoDShape and Triangle classes that uses
methods to access the private instance variables width and height:


Ask the Expert
Q
: When should I make an instance variable private?
A
: There are no hard and fast rules, but here are two general principles. If an
instance variable is to be used only by methods defined within its class, then it
should be made private. If an instance variable must be within certain bounds,
then it should be private and made available only through accessor methods. This
way, you can prevent invalid values from being assigned.
CONSTRUCTORS AND INHERITANCE
In a hierarchy, it is possible for both superclasses and subclasses to have their own
constructors. This raises an important question: What constructor is responsible for
building an object of the subclass—the one in the superclass, the one in the subclass, or
both? The answer is this: The constructor for the superclass constructs the superclass
portion of the object, and the constructor for the subclass constructs the subclass part.
This makes sense because the superclass has no knowledge of or access to any element
in a subclass. Thus, their construction must be separate. The preceding examples have
relied upon the default constructors created automatically by Java, so this was not an


issue. However, in practice, most classes will have explicit constructors. Here you will
see how to handle this situation.
When only the subclass defines a constructor, the process is straightforward: simply
construct the subclass object. The superclass portion of the object is constructed
automatically using its default constructor. For example, here is a reworked version of
Triangle that defines a constructor. It also makes style private, since it is now set by
the constructor.


Here, Triangle’s constructor initializes the members of TwoDClass that it inherits
along with its own style field.


When both the superclass and the subclass define constructors, the process is a bit
more complicated because both the superclass and subclass constructors must be
executed. In this case, you must use another of Java’s keywords, super, which has two
general forms. The first calls a superclass constructor. The second is used to access a
member of the superclass that has been hidden by a member of a subclass. Here, we
will look at its first use.
USING SUPER TO CALL SUPERCLASS
CONSTRUCTORS
A subclass can call a constructor defined by its superclass by use of the following form
of super:
super(parameter­list
Here, parameter­list specifies any parameters needed by the constructor in the
superclass. super( ) must always be the first statement executed inside a subclass
constructor. To see how super( ) is used, consider the version of TwoDShape in the
following program. It defines a constructor that initializes width and height.


Here, Triangle( ) calls super( ) with the parameters w and h. This causes the
TwoDShape( ) constructor to be called, which initializes width and height using
these values. Triangle no longer initializes these values itself. It need only initialize the
value unique to it: style. This leaves TwoDShape free to construct its subobject in any
manner that it so chooses. Furthermore, TwoDShape can add functionality about
which existing subclasses have no knowledge, thus preventing existing code from
breaking.
Any form of constructor defined by the superclass can be called by super( ). The
constructor executed will be the one that matches the arguments. For example, here are
expanded versions of both TwoDShape and Triangle that include default


constructors and constructors that take one argument:


Here is the output from this version:


Let’s review the key concepts behind super( ). When a subclass calls super( ), it is
calling the constructor of its immediate superclass. Thus, super( ) always refers to the
superclass immediately above the calling class. This is true even in a multilevel
hierarchy. Also, super( ) must always be the first statement executed inside a subclass
constructor.
USING SUPER TO ACCESS SUPERCLASS
MEMBERS
There is a second form of super that acts somewhat like this, except that it always
refers to the superclass of the subclass in which it is used. This usage has the following
general form:
super.member
Here, member can be either a method or an instance variable.
This form of super is most applicable to situations in which member names of a
subclass hide members by the same name in the superclass. Consider this simple class
hierarchy:


This program displays the following:
Although the instance variable i in B hides the i in A, super allows access to the i
defined in the superclass. super can also be used to call methods that are hidden by a
subclass.
Try This 7­1
Extending the Vehicle Class
To illustrate the power of inheritance, we will extend the Vehicle class first developed
in 
Chapter 4
. As you should recall, Vehicle encapsulates information about vehicles,
including the number of passengers they can carry, their fuel capacity, and their fuel


consumption rate. We can use the Vehicle class as a starting point from which more
specialized classes are developed. For example, one type of vehicle is a truck. An
important attribute of a truck is its cargo capacity. Thus, to create a Truck class, you
can extend Vehicle, adding an instance variable that stores the carrying capacity. Here
is a version of Truck that does this. In the process, the instance variables in Vehicle
will be made private, and accessor methods are provided to get and set their values.
1. Create a file called TruckDemo.java and copy the last implementation of Vehicle
from 
Chapter 4
into the file:
2. Create the Truck class as shown here:
Here, Truck inherits Vehicle, adding cargocap, getCargo( ), and putCargo( ).
Thus, Truck includes all of the general vehicle attributes defined by Vehicle. It need
add only those items that are unique to its own class.
3. Next, make the instance variables of Vehicle private, as shown here:
4. Here is an entire program that demonstrates the Truck class:



5. The output from this program is shown here:
6. Many other types of classes can be derived from Vehicle. For example, the
following skeleton creates an off­road class that stores the ground clearance of the
vehicle.
The key point is that once you have created a superclass that defines the general
aspects of an object, that superclass can be inherited to form specialized classes. Each
subclass simply adds its own, unique attributes. This is the essence of inheritance.
CREATING A MULTILEVEL HIERARCHY


CREATING A MULTILEVEL HIERARCHY
Up to this point, we have been using simple class hierarchies that consist of only a
superclass and a subclass. However, you can build hierarchies that contain as many
layers of inheritance as you like. As mentioned, it is perfectly acceptable to use a
subclass as a superclass of another. For example, given three classes called A, B, and C,
C can be a subclass of B, which is a subclass of A. When this type of situation occurs,
each subclass inherits all of the traits found in all of its superclasses. In this case, C
inherits all aspects of B and A.
To see how a multilevel hierarchy can be useful, consider the following program. In it,
the subclass Triangle is used as a superclass to create the subclass called
ColorTriangle. ColorTriangle inherits all of the traits of Triangle and
TwoDShape and adds a field called color, which holds the color of the triangle.



The output of this program is shown here:
Because of inheritance, ColorTriangle can make use of the previously defined classes
of Triangle and TwoDShape, adding only the extra information it needs for its own,
specific application. This is part of the value of inheritance; it allows the reuse of code.
This example illustrates one other important point: super( ) always refers to the
constructor in the closest superclass. The super( ) in ColorTriangle calls the
constructor in Triangle. The super( ) in Triangle calls the constructor in
TwoDShape. In a class hierarchy, if a superclass constructor requires parameters,
then all subclasses must pass those parameters “up the line.” This is true whether or not
a subclass needs parameters of its own.
WHEN ARE CONSTRUCTORS EXECUTED?
In the foregoing discussion of inheritance and class hierarchies, an important question
may have occurred to you: When a subclass object is created, whose constructor is
executed first, the one in the subclass or the one defined by the superclass? For
example, given a subclass called B and a superclass called A, is A’s constructor


executed before B’s, or vice versa? The answer is that in a class hierarchy, constructors
complete their execution in order of derivation, from superclass to subclass. Further,
since super( ) must be the first statement executed in a subclass’ constructor, this
order is the same whether or not super( ) is used. If super( ) is not used, then the
default (parameterless) constructor of each superclass will be executed. The following
program illustrates when constructors are executed:
The output from this program is shown here:
As you can see, the constructors are executed in order of derivation.


If you think about it, it makes sense that constructors are executed in order of
derivation. Because a superclass has no knowledge of any subclass, any initialization it
needs to perform is separate from and possibly prerequisite to any initialization
performed by the subclass. Therefore, it must complete its execution first.
SUPERCLASS REFERENCES AND SUBCLASS
OBJECTS
As you know, Java is a strongly typed language. Aside from the standard conversions
and automatic promotions that apply to its primitive types, type compatibility is strictly
enforced. Therefore, a reference variable for one class type cannot normally refer to an
object of another class type. For example, consider the following program:
Here, even though class X and class Y are structurally the same, it is not possible to
assign an X reference to a Y object because they have different types. In general, an
object reference variable can refer only to objects of its type.


There is, however, an important exception to Java’s strict type enforcement. A
reference variable of a superclass can be assigned a reference to an object of any
subclass derived from that superclass. In other words, a superclass reference can refer
to a subclass object. Here is an example:
Here, Y is now derived from X; thus, it is permissible for x2 to be assigned a reference
to a Y object.
It is important to understand that it is the type of the reference variable—not the type of
the object that it refers to—that determines what members can be accessed. That is,
when a reference to a subclass object is assigned to a superclass reference variable, you
will have access only to those parts of the object defined by the superclass. This is why
x2 can’t access b even when it refers to a Y object. If you think about it, this makes
sense, because the superclass has no knowledge of what a subclass adds to it. This is
why the last line of code in the program is commented out.


Although the preceding discussion may seem a bit esoteric, it has some important
practical applications. One is described here. The other is discussed later in this
chapter, when method overriding is covered.
An important place where subclass references are assigned to superclass variables is
when constructors are called in a class hierarchy. As you know, it is common for a class
to define a constructor that takes an object of the class as a parameter. This allows the
class to construct a copy of an object. Subclasses of such a class can take advantage of
this feature. For example, consider the following versions of TwoDShape and
Triangle. Both add constructors that take an object as a parameter.



In this program, t2 is constructed from t1 and is, thus, identical. The output is shown
here:
Pay special attention to this Triangle constructor:
It receives an object of type Triangle and it passes that object (through super) to this
TwoDShape constructor:
The key point is that TwoDshape( ) is expecting a TwoDShape object. However,
Triangle( ) passes it a Triangle object. The reason this works is because, as
explained, a superclass reference can refer to a subclass object. Thus, it is perfectly
acceptable to pass TwoDShape( ) a reference to an object of a class derived from
TwoDShape. Because the TwoDShape( ) constructor is initializing only those
portions of the subclass object that are members of TwoDShape, it doesn’t matter that


the object might also contain other members added by derived classes.
METHOD OVERRIDING
In a class hierarchy, when a method in a subclass has the same return type and
signature as a method in its superclass, then the method in the subclass is said to

Yüklə 83 Mb.

Dostları ilə paylaş:
1   ...   31   32   33   34   35   36   37   38   ...   82




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə