System, but they may not be reproduced for publication



Yüklə 83 Mb.
Pdf görüntüsü
səhifə60/82
tarix19.04.2023
ölçüsü83 Mb.
#106251
1   ...   56   57   58   59   60   61   62   63   ...   82
Java A Beginner’s Guide, Eighth Edition ( PDFDrive )

specific type of generic interface, such as shown here:
then the implementing class does not need to be generic.
As you might expect, the type parameter(s) specified by a generic interface can be
bounded. This lets you limit the type of data for which the interface can be
implemented. For example, if you wanted to limit Containment to numeric types,
then you could declare it like this:
Now, any implementing class must pass to Containment a type argument also having
the same bound. For example, now MyClass must be declared as shown here:
Pay special attention to the way the type parameter T is declared by MyClass and then
passed to Containment. Because Containment now requires a type that extends


Number, the implementing class (MyClass in this case) must specify the same
bound. Furthermore, once this bound has been established, there is no need to specify
it again in the implements clause. In fact, it would be wrong to do so. For example,
this declaration is incorrect and won’t compile:
Once the type parameter has been established, it is simply passed to the interface
without further modification.
Here is the generalized syntax for a generic interface:
interface interface­name<type­param­list> { // ...
Here, type­param­list is a comma­separated list of type parameters. When a generic
interface is implemented, you must specify the type arguments, as shown here:
Try This 13­1
Create a Generic Queue
One of the most powerful advantages that generics bring to programming is the ability
to construct reliable, reusable code. As mentioned at the start of this chapter, many
algorithms are the same no matter what type of data they are used on. For example, a
queue works the same way whether that queue is for integers, strings, or File objects.
Instead of creating a separate queue class for each type of object, you can craft a single,
generic solution that can be used with any type of object. Thus, the development cycle
of design, code, test, and debug occurs only once when you create a generic solution—
not repeatedly, each time a queue is needed for a new data type.
In this project, you will adapt the queue example that has been evolving since 
Try This
5­2
, making it generic. This project represents the final evolution of the queue. It


includes a generic interface that defines the queue operations, two exception classes,
and one queue implementation: a fixed­size queue. Of course, you can experiment with
other types of generic queues, such as a generic dynamic queue or a generic circular
queue. Just follow the lead of the example shown here.
Like the previous version of the queue shown in 
Try This 9­1
, this project organizes the
queue code into a set of separate files: one for the interface, one for each queue
exception, one for the fixed­queue implementation, and one for the program that
demonstrates it. This organization reflects the way that this project would normally be
organized in the real world.
1. The first step in creating a generic queue is to create a generic interface that
describes the queue’s two operations: put and get. The generic version of the queue
interface is called IGenQ and it is shown here. Put this interface into a file called
IGenQ.java.
Notice that the type of data stored by the queue is specified by the generic type
parameter T.
2. Next, create the files QueueFullException.java and
QueueEmptyException.java. Put in each file its corresponding class, shown here:


These classes encapsulate the two queue errors: full or empty. They are not generic
classes because they are the same no matter what type of data is stored in a queue.
Thus, these two files will be the same as those you used with 
Try This 9­1
.
3. Now, create a file called GenQueue.java. Into that file, put the following code,
which implements a fixed­size queue:


GenQueue is a generic class with type parameter T, which specifies the type of data
stored in the queue. Notice that T is also passed to the IGenQ interface.
Notice that the GenQueue constructor is passed a reference to an array that will be
used to hold the queue. Thus, to construct a GenQueue, you will first create an array
whose type is compatible with the objects that you will be storing in the queue and
whose size is long enough to store the number of objects that will be placed in the
queue.
For example, the following sequence shows how to create a queue that holds strings:


4. Create a file called GenQDemo.java and put the following code into it. This
program demonstrates the generic queue.


5. Compile the program and run it. You will see the output shown here:


6. On your own, try converting the CircularQueue and DynQueue classes from 
Try
This 8­1
into generic classes.
RAW TYPES AND LEGACY CODE
Because support for generics did not exist prior to JDK 5, it was necessary for Java to
provide some transition path from old, pre­generics code. Simply put, pre­generics
legacy code had to remain both functional and compatible with generics. This meant
that pre­generics code must be able to work with generics, and generic code must be
able to work with pre­generics code.
To handle the transition to generics, Java allows a generic class to be used without any
type arguments. This creates a raw type for the class. This raw type is compatible with
legacy code, which has no knowledge of generics. The main drawback to using the raw
type is that the type safety of generics is lost.


Here is an example that shows a raw type in action:
This program contains several interesting things. First, a raw type of the generic Gen
class is created by the following declaration:


Notice that no type arguments are specified. In essence, this creates a Gen object
whose type T is replaced by Object.
A raw type is not type safe. Thus, a variable of a raw type can be assigned a reference to
any type of Gen object. The reverse is also allowed, in which a variable of a specific
Gen type can be assigned a reference to a raw Gen object. However, both operations
are potentially unsafe because the type checking mechanism of generics is
circumvented.
This lack of type safety is illustrated by the commented­out lines at the end of the
program. Let’s examine each case. First, consider the following situation:
In this statement, the value of ob inside raw is obtained, and this value is cast to
Integer. The trouble is that raw contains a Double value, not an integer value.
However, this cannot be detected at compile time because the type of raw is unknown.
Thus, this statement fails at run time.
The next sequence assigns to strOb (a reference of type Gen) a reference to
a raw Gen object:
The assignment itself is syntactically correct, but questionable. Because strOb is of
type Gen, it is assumed to contain a String. However, after the assignment,
the object referred to by strOb contains a Double. Thus, at run time, when an attempt
is made to assign the contents of strOb to str, a run­time error results because strOb
now contains a Double. Thus, the assignment of a raw reference to a generic reference
bypasses the type­safety mechanism.
Here, a generic reference is assigned to a raw reference variable. Although this is
syntactically correct, it can lead to problems, as illustrated by the second line. In this
case, raw now refers to an object that contains an Integer object, but the cast assumes
that it contains a Double. This error cannot be prevented at compile time. Rather, it
causes a run­time error.


Because of the potential for danger inherent in raw types, javac displays unchecked

Yüklə 83 Mb.

Dostları ilə paylaş:
1   ...   56   57   58   59   60   61   62   63   ...   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ə