System, but they may not be reproduced for publication



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

access interface name {
ret­type method­name1(param­list
ret­type method­name2(param­list
type var1 = value;
type var2 = value;
// ...
ret­type method­nameN(param­list
type varN = value;
}
For a top­level interface, access is either public or not used. When no access modifier
is included, then default access results, and the interface is available only to other
members of its package. When it is declared as public, the interface can be used by any
other code. (When an interface is declared public, it must be in a file of the same
name.) name is the name of the interface and can be any valid identifier, except for var
(which is a reserved type name added by JDK 10).
In the traditional form of an interface, methods are declared using only their return
type and signature. They are, essentially, abstract methods. Thus, each class that
includes such an interface must implement all of its methods. In an interface,
methods are implicitly public.
Variables declared in an interface are not instance variables. Instead, they are
implicitly public, final, and static and must be initialized. Thus, they are essentially
constants.
Here is an example of an interface definition. It specifies the interface to a class that
generates a series of numbers.


This interface is declared public so that it can be implemented by code in any package.
IMPLEMENTING INTERFACES
Once an interface has been defined, one or more classes can implement that interface.
To implement an interface, include the implements clause in a class definition and
then create the methods required by the interface. The general form of a class that
includes the implements clause looks like this:
class classname extends superclass implements interface {
// class­body
}
To implement more than one interface, the interfaces are separated with a comma. Of
course, the extends clause is optional.
The methods that implement an interface must be declared public. Also, the type
signature of the implementing method must match exactly the type signature specified
in the interface definition.
Here is an example that implements the Series interface shown earlier. It creates a
class called ByTwos, which generates a series of numbers, each two greater than the
previous one.


Notice that the methods getNext( ), reset( ), and setStart( ) are declared using the
public access specifier. This is necessary. Whenever you implement a method defined
by an interface, it must be implemented as public because all members of an interface
are implicitly public.
Here is a class that demonstrates ByTwos:


The output from this program is shown here:


It is both permissible and common for classes that implement interfaces to define
additional members of their own. For example, the following version of ByTwos adds
the method getPrevious( ), which returns the previous value:
Notice that the addition of getPrevious( ) required a change to the implementations
of the methods defined by Series. However, since the interface to those methods stays
the same, the change is seamless and does not break preexisting code. This is one of the
advantages of interfaces.
As explained, any number of classes can implement an interface. For example, here is


a class called ByThrees that generates a series that consists of multiples of three:
One more point: If a class includes an interface but does not fully implement the
methods defined by that interface, then that class must be declared abstract. No
objects of such a class can be created, but it can be used as an abstract superclass,
allowing subclasses to provide the complete implementation.
USING INTERFACE REFERENCES
You might be somewhat surprised to learn that you can declare a reference variable of
an interface type. In other words, you can create an interface reference variable. Such a
variable can refer to any object that implements its interface. When you call a method
on an object through an interface reference, it is the version of the method
implemented by the object that is executed. This process is similar to using a superclass
reference to access a subclass object, as described in 
Chapter 7
.
The following example illustrates this process. It uses the same interface reference
variable to call methods on objects of both ByTwos and ByThrees.



In main( ), ob is declared to be a reference to a Series interface. This means that it
can be used to store references to any object that implements Series. In this case, it is
used to refer to twoOb and threeOb, which are objects of type ByTwos and
ByThrees, respectively, which both implement Series. An interface reference variable
has knowledge only of the methods declared by its interface declaration. Thus, ob
could not be used to access any other variables or methods that might be supported by
the object.
Try This 8­1
Creating a Queue Interface
To see the power of interfaces in action, we will look at a practical example. In earlier
chapters, you developed a class called Queue that implemented a simple fixed­size
queue for characters. However, there are many ways to implement a queue. For
example, the queue can be of a fixed size or it can be “growable.” The queue can be
linear, in which case it can be used up, or it can be circular, in which case elements can
be put in as long as elements are being taken off. The queue can also be held in an
array, a linked list, a binary tree, and so on. No matter how the queue is implemented,
the interface to the queue remains the same, and the methods put( ) and get( ) define
the interface to the queue independently of the details of the implementation. Because
the interface to a queue is separate from its implementation, it is easy to define a queue
interface, leaving it to each implementation to define the specifics.
In this project, you will create an interface for a character queue and three
implementations. All three implementations will use an array to store the characters.
One queue will be the fixed­size, linear queue developed earlier. Another will be a
circular queue. In a circular queue, when the end of the underlying array is
encountered, the get and put indices automatically loop back to the start. Thus, any
number of items can be stored in a circular queue as long as items are also being taken
out. The final implementation creates a dynamic queue, which grows as necessary
when its size is exceeded.
1. Create a file called ICharQ.java and put into that file the following interface


definition:
As you can see, this interface is very simple, consisting of only two methods. Each class
that implements ICharQ will need to implement these methods.
2. Create a file called IQDemo.java.
3. Begin creating IQDemo.java by adding the FixedQueue class shown here:


This implementation of ICharQ is adapted from the Queue class shown in 
Chapter 5
and should already be familiar to you.
4. To IQDemo.java add the CircularQueue class shown here. It implements a
circular queue for characters.


The circular queue works by reusing space in the array that is freed when elements are
retrieved. Thus, it can store an unlimited number of elements as long as elements are
also being removed. While conceptually simple—just reset the appropriate index to
zero when the end of the array is reached—the boundary conditions are a bit confusing
at first. In a circular queue, the queue is full not when the end of the underlying array is


reached, but rather when storing an item would cause an unretrieved item to be
overwritten. Thus, put( ) must check several conditions in order to determine if the
queue is full. As the comments suggest, the queue is full when either putloc is one less
than getloc, or if putloc is at the end of the array and getloc is at the beginning. As
before, the queue is empty when getloc and putloc are equal. To make these checks
easier, the underlying array is created one size larger than the queue size.
5. Put into IQDemo.java the DynQueue class shown next. It implements a
“growable” queue that expands its size when space is exhausted.


In this queue implementation, when the queue is full, an attempt to store another
element causes a new underlying array to be allocated that is twice as large as the
original, the current contents of the queue are copied into this array, and a reference to
the new array is stored in q.
6. To demonstrate the three ICharQ implementations, enter the following class into


IQDemo.java. It uses an ICharQ reference to access all three queues.


7. The output from this program is shown here:
8. Here are some things to try on your own. Create a circular version of DynQueue.
Add a reset( ) method to ICharQ, which resets the queue. Create a static method


that copies the contents of one type of queue into another.
VARIABLES IN INTERFACES
As mentioned, variables can be declared in an interface, but they are implicitly public,
static, and final. At first glance, you might think that there would be very limited use
for such variables, but the opposite is true. Large programs typically make use of
several constant values that describe such things as array size, various limits, special
values, and the like. Since a large program is typically held in a number of separate
source files, there needs to be a convenient way to make these constants available to
each file. In Java, interface variables offer one solution.
To define a set of shared constants, create an interface that contains only these
constants, without any methods. Each file that needs access to the constants simply
“implements” the interface. This brings the constants into view. Here is an example:
NOTE


The technique of using an interface to define shared constants is controversial. It is
described here for completeness.
INTERFACES CAN BE EXTENDED
One interface can inherit another by use of the keyword extends. The syntax is the
same as for inheriting classes. When a class implements an interface that inherits
another interface, it must provide implementations for all methods required by the
interface inheritance chain. Following is an example:


As an experiment, you might try removing the implementation for meth1( ) in
MyClass. This will cause a compile­time error. As stated earlier, any class that
implements an interface must implement all methods required by that interface,
including any that are inherited from other interfaces.
DEFAULT INTERFACE METHODS
As explained earlier, prior to JDK 8, an interface could not define any implementation


whatsoever. This meant that for all previous versions of Java, the methods specified by
an interface were abstract, containing no body. This is the traditional form of an
interface and is the type of interface that the preceding discussions have used. The
release of JDK 8 changed this by adding a new capability to interface called the

Yüklə 83 Mb.

Dostları ilə paylaş:
1   ...   35   36   37   38   39   40   41   42   ...   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ə