Tải bản đầy đủ (.pdf) (119 trang)

Thinking in Java 3rd Edition phần 4 pptx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (538.55 KB, 119 trang )


Chapter 7: Polymorphism 327
Shape
draw()
erase()
Circle
draw()
erase()
Square
draw()
erase()
Triangle
draw()
erase()

This can be called a pure “is-a” relationship because the interface of a
class establishes what it is. Inheritance guarantees that any derived class
will have the interface of the base class and nothing less. If you follow the
above diagram, derived classes will also have no more than the base class
interface.
Feedback

This can be thought of as pure substitution, because derived class objects
can be perfectly substituted for the base class, and you never need to
know any extra information about the subclasses when you’re using them:
Circle, Square,
Line, or new type
of Shape
Talks to Shape
Message
"Is-a"


relationship

That is, the base class can receive any message you can send to the
derived class because the two have exactly the same interface. All you
need to do is upcast from the derived class and never look back to see
what exact type of object you’re dealing with. Everything is handled
through polymorphism.
Feedback

When you see it this way, it seems like a pure “is-a” relationship is the
only sensible way to do things, and any other design indicates muddled
thinking and is by definition broken. This too is a trap. As soon as you
start thinking this way, you’ll turn around and discover that extending the
interface (which, unfortunately, the keyword extends seems to
encourage) is the perfect solution to a particular problem. This could be

328 Thinking in Java www.BruceEckel.com
termed an “is-like-a” relationship because the derived class is like the base
class—it has the same fundamental interface—but it has other features
that require additional methods to implement:
Useful
void f()
void g()
void f()
void g()
void u()
void v()
void w()
MoreUseful
}

Assume this
represents a big
interface
"Is-like-a"
}
Extending
the interface

While this is also a useful and sensible approach (depending on the
situation) it has a drawback. The extended part of the interface in the
derived class is not available from the base class, so once you upcast you
can’t call the new methods:
Useful part
Talks to Useful
object
Message
MoreUseful
part

If you’re not upcasting in this case, it won’t bother you, but often you’ll get
into a situation in which you need to rediscover the exact type of the
object so you can access the extended methods of that type. The following
section shows how this is done.
Feedback


Chapter 7: Polymorphism 329
Downcasting and run time
type identification
Since you lose the specific type information via an upcast (moving up the

inheritance hierarchy), it makes sense that to retrieve the type
information—that is, to move back down the inheritance hierarchy—you
use a downcast. However, you know an upcast is always safe; the base
class cannot have a bigger interface than the derived class, therefore every
message you send through the base class interface is guaranteed to be
accepted. But with a downcast, you don’t really know that a shape (for
example) is actually a circle. It could instead be a triangle or square or
some other type.
Feedback

Useful
void f()
void g()
void f()
void g()
void u()
void v()
void w()
MoreUseful
}
Assume this
represents a big
interface
"Is-like-a"
}
Extending
the interface

To solve this problem there must be some way to guarantee that a
downcast is correct, so you won’t accidentally cast to the wrong type and

then send a message that the object can’t accept. This would be quite
unsafe.
Feedback

In some languages (like C++) you must perform a special operation in
order to get a type-safe downcast, but in Java every cast is checked! So
even though it looks like you’re just performing an ordinary parenthesized
cast, at run time this cast is checked to ensure that it is in fact the type you
think it is. If it isn’t, you get a ClassCastException. This act of checking

330 Thinking in Java www.BruceEckel.com
types at run time is called run-time type identification (RTTI). The
following example demonstrates the behavior of RTTI:
//: c07:RTTI.java
// Downcasting & Run-Time Type Identification (RTTI).
// {ThrowsException}

class Useful {
public void f() {}
public void g() {}
}

class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
}


public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile time: method not found in Useful:
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
} ///:~

As in the diagram, MoreUseful extends the interface of Useful. But
since it’s inherited, it can also be upcast to a Useful. You can see this
happening in the initialization of the array x in main( ). Since both
objects in the array are of class Useful, you can send the f( ) and g( )
methods to both, and if you try to call u( ) (which exists only in
MoreUseful) you’ll get a compile-time error message.
Feedback

If you want to access the extended interface of a MoreUseful object, you
can try to downcast. If it’s the correct type, it will be successful. Otherwise,

Chapter 7: Polymorphism 331
you’ll get a ClassCastException. You don’t need to write any special
code for this exception, since it indicates a programmer error that could
happen anywhere in a program.

Feedback

There’s more to RTTI than a simple cast. For example, there’s a way to see
what type you’re dealing with before you try to downcast it. All of Chapter
10 is devoted to the study of different aspects of Java run-time type
identification.
Feedback

Summary
Polymorphism means “different forms.” In object-oriented programming,
you have the same face (the common interface in the base class) and
different forms using that face: the different versions of the dynamically
bound methods.
Feedback

You’ve seen in this chapter that it’s impossible to understand, or even
create, an example of polymorphism without using data abstraction and
inheritance. Polymorphism is a feature that cannot be viewed in isolation
(like a switch statement can, for example), but instead works only in
concert, as part of a “big picture” of class relationships. People are often
confused by other, non-object-oriented features of Java, like method
overloading, which are sometimes presented as object-oriented. Don’t be
fooled: If it isn’t late binding, it isn’t polymorphism.
Feedback

To use polymorphism—and thus object-oriented techniques—effectively
in your programs you must expand your view of programming to include
not just members and messages of an individual class, but also the
commonality among classes and their relationships with each other.
Although this requires significant effort, it’s a worthy struggle, because

the results are faster program development, better code organization,
extensible programs, and easier code maintenance.
Feedback

Exercises
Solutions to selected exercises can be found in the electronic document The Thinking in Java
Annotated Solution Guide, available for a small fee from www.BruceEckel.com.


332 Thinking in Java www.BruceEckel.com
1. Add a new method in the base class of Shapes.java that prints a
message, but don’t override it in the derived classes. Explain what
happens. Now override it in one of the derived classes but not the
others, and see what happens. Finally, override it in all the derived
classes.
Feedback

2. Add a new type of Shape to Shapes.java and verify in main( )
that polymorphism works for your new type as it does in the old
types.
Feedback

3. Change Music3.java so that what( ) becomes the root Object
method toString( ). Try printing the Instrument objects using
System.out.println( ) (without any casting).
Feedback

4. Add a new type of Instrument to Music3.java and verify that
polymorphism works for your new type.
Feedback


5. Modify Music3.java so that it randomly creates Instrument
objects the way Shapes.java does.
Feedback

6. Create an inheritance hierarchy of Rodent: Mouse, Gerbil,
Hamster, etc. In the base class, provide methods that are
common to all Rodents, and override these in the derived classes
to perform different behaviors depending on the specific type of
Rodent. Create an array of Rodent, fill it with different specific
types of Rodents, and call your base-class methods to see what
happens.
Feedback

7. Modify Exercise 6 so that Rodent is an abstract class. Make the
methods of Rodent abstract whenever possible.
Feedback

8. Create a class as abstract without including any abstract
methods, and verify that you cannot create any instances of that
class.
Feedback

9. Add class Pickle to Sandwich.java.
Feedback

10. Modify Exercise 6 so that it demonstrates the order of
initialization of the base classes and derived classes. Now add
member objects to both the base and derived classes, and show the


Chapter 7: Polymorphism 333
order in which their initialization occurs during construction.
Feedback

11. Create a base class with two methods. In the first method, call the
second method. Inherit a class and override the second method.
Create an object of the derived class, upcast it to the base type, and
call the first method. Explain what happens.
Feedback

12. Create a base class with an abstract print( ) method that is
overridden in a derived class. The overridden version of the
method prints the value of an int variable defined in the derived
class. At the point of definition of this variable, give it a nonzero
value. In the base-class constructor, call this method. In main( ),
create an object of the derived type, and then call its print( )
method. Explain the results.
Feedback

13. Following the example in Transmogrify.java, create a Starship
class containing an AlertStatus reference that can indicate three
different states. Include methods to change the states.
Feedback

14. Create an abstract class with no methods. Derive a class and add
a method. Create a static method that takes a reference to the
base class, downcasts it to the derived class, and calls the method.
In main( ), demonstrate that it works. Now put the abstract
declaration for the method in the base class, thus eliminating the
need for the downcast.

Feedback




335
8: Interfaces &
Inner Classes
Interfaces and inner classes provide more sophisticated
ways to organize and control the objects in your system.
C++, for example, does not contain such mechanisms, although the clever
programmer may simulate them. The fact that they exist in Java indicates
that they were considered important enough to provide direct support
through language keywords.
Feedback

In Chapter 7, you learned about the abstract keyword, which allows you
to create one or more methods in a class that have no definitions—you
provide part of the interface without providing a corresponding
implementation, which is created by inheritors. The interface keyword
produces a completely abstract class, one that provides no
implementation at all. You’ll learn that the interface is more than just an
abstract class taken to the extreme, since it allows you to perform a
variation on C++’s “multiple inheritance,” by creating a class that can be
upcast to more than one base type.
Feedback

At first, inner classes look like a simple code-hiding mechanism: you place
classes inside other classes. You’ll learn, however, that the inner class
does more than that—it knows about and can communicate with the

surrounding class—and that the kind of code you can write with inner
classes is more elegant and clear, although it is a new concept to most. It
takes some time to become comfortable with design using inner classes.
Feedback

Interfaces
The interface keyword takes the abstract concept one step further. You
could think of it as a “pure” abstract class. It allows the creator to
establish the form for a class: method names, argument lists, and return

336 Thinking in Java www.BruceEckel.com
types, but no method bodies. An interface can also contain fields, but
these are implicitly static and final. An interface provides only a form,
but no implementation.
Feedback

An interface says: “This is what all classes that implement this particular
interface will look like.” Thus, any code that uses a particular interface
knows what methods might be called for that interface, and that’s all. So
the interface is used to establish a “protocol” between classes. (Some
object-oriented programming languages have a keyword called protocol
to do the same thing.)
Feedback

To create an interface, use the interface keyword instead of the class
keyword. Like a class, you can add the public keyword before the
interface keyword (but only if that interface is defined in a file of the
same name) or leave it off to give package access, so that it is only usable
within the same package.
Feedback


To make a class that conforms to a particular interface (or group of
interfaces) use the implements keyword. implements says “The
interface is what it looks like, but now I’m going to say how it works.”
Other than that, it looks like inheritance. The diagram for the instrument
example shows this:

Chapter 8: Interfaces & Inner Classes 337
interface Instrument
void play();
String what();
void adjust();
Wind
void play()
String what()
void adjust()
Stringed
void play()
String what()
void adjust()
Woodwind
void play()
String what()
Brass
void play()
void adjust()
Percussion
void play()
String what()
void adjust()

extendsextends
implements implements implements

You can see from the Woodwind and Brass classes that once you’ve
implemented an interface, that implementation becomes an ordinary
class that can be extended in the regular way.
Feedback

You can choose to explicitly declare the method declarations in an
interface as public. But they are public even if you don’t say it. So
when you implement an interface, the methods from the interface
must be defined as public. Otherwise they would default to package
access, and you’d be reducing the accessibility of a method during
inheritance, which is not allowed by the Java compiler.
Feedback

You can see this in the modified version of the Instrument example.
Note that every method in the interface is strictly a declaration, which is
the only thing the compiler allows. In addition, none of the methods in
Instrument are declared as public, but they’re automatically public
anyway:

338 Thinking in Java www.BruceEckel.com
//: c08:music5:Music5.java
// Interfaces.
package c08.music5;
import com.bruceeckel.simpletest.*;
import c07.music.Note;

interface Instrument {

// Compile-time constant:
int i = 5; // static & final
// Cannot have method definitions:
void play(Note n); // Automatically public
String what();
void adjust();
}

class Wind implements Instrument {
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
public String what() { return "Wind"; }
public void adjust() {}
}

class Percussion implements Instrument {
public void play(Note n) {
System.out.println("Percussion.play() " + n);
}
public String what() { return "Percussion"; }
public void adjust() {}
}

class Stringed implements Instrument {
public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
public String what() { return "Stringed"; }
public void adjust() {}

}

class Brass extends Wind {
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
public void adjust() {

Chapter 8: Interfaces & Inner Classes 339
System.out.println("Brass.adjust()");
}
}

class Woodwind extends Wind {
public void play(Note n) {
System.out.println("Woodwind.play() " + n);
}
public String what() { return "Woodwind"; }
}

public class Music5 {
private static Test monitor = new Test();
// Doesn't care about type, so new types
// added to the system still work right:
static void tune(Instrument i) {
//
i.play(Note.MIDDLE_C);
}
static void tuneAll(Instrument[] e) {
for(int i = 0; i < e.length; i++)

tune(e[i]);
}
public static void main(String[] args) {
// Upcasting during addition to the array:
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
monitor.expect(new String[] {
"Wind.play() Middle C",
"Percussion.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C",
"Woodwind.play() Middle C"
});
}
} ///:~


340 Thinking in Java www.BruceEckel.com
The rest of the code works the same. It doesn’t matter if you are upcasting
to a “regular” class called Instrument, an abstract class called
Instrument, or to an interface called Instrument. The behavior is the
same. In fact, you can see in the tune( ) method that there isn’t any
evidence about whether Instrument is a “regular” class, an abstract
class, or an interface. This is the intent: Each approach gives the

programmer different control over the way objects are created and used.
Feedback

“Multiple inheritance” in Java
The interface isn’t simply a “more pure” form of abstract class. It has a
higher purpose than that. Because an interface has no implementation
at all—that is, there is no storage associated with an interface—there’s
nothing to prevent many interfaces from being combined. This is
valuable because there are times when you need to say “An x is an a and a
b and a c.” In C++, this act of combining multiple class interfaces is called
multiple inheritance, and it carries some rather sticky baggage because
each class can have an implementation. In Java, you can perform the
same act, but only one of the classes can have an implementation, so the
problems seen in C++ do not occur with Java when combining multiple
interfaces:
Abstract or Concrete
Base Class
interface 1
interface 2
interface n
Base Class Methods interface 1

interface 2 interface n
.
.
.
.
.
.


In a derived class, you aren’t forced to have a base class that is either an
abstract or “concrete” (one with no abstract methods). If you do inherit
from a non-interface, you can inherit from only one. All the rest of the
base elements must be interfaces. You place all the interface names after
the implements keyword and separate them with commas. You can have
as many interfaces as you want—each one becomes an independent type

Chapter 8: Interfaces & Inner Classes 341
that you can upcast to. The following example shows a concrete class
combined with several interfaces to produce a new class:
Feedback

//: c08:Adventure.java
// Multiple interfaces.

interface CanFight {
void fight();
}

interface CanSwim {
void swim();
}

interface CanFly {
void fly();
}

class ActionCharacter {
public void fight() {}
}


class Hero extends ActionCharacter
implements CanFight, CanSwim, CanFly {
public void swim() {}
public void fly() {}
}

public class Adventure {
public static void t(CanFight x) { x.fight(); }
public static void u(CanSwim x) { x.swim(); }
public static void v(CanFly x) { x.fly(); }
public static void w(ActionCharacter x) { x.fight(); }
public static void main(String[] args) {
Hero h = new Hero();
t(h); // Treat it as a CanFight
u(h); // Treat it as a CanSwim
v(h); // Treat it as a CanFly
w(h); // Treat it as an ActionCharacter
}
} ///:~

You can see that Hero combines the concrete class ActionCharacter
with the interfaces CanFight, CanSwim, and CanFly. When you

342 Thinking in Java www.BruceEckel.com
combine a concrete class with interfaces this way, the concrete class must
come first, then the interfaces. (The compiler gives an error otherwise.)
Feedback

Note that the signature for fight( ) is the same in the interface

CanFight and the class ActionCharacter, and that fight( ) is not
provided with a definition in Hero. The rule for an interface is that you
can inherit from it (as you will see shortly), but then you’ve got another
interface. If you want to create an object of the new type, it must be a
class with all definitions provided. Even though Hero does not explicitly
provide a definition for fight( ), the definition comes along with
ActionCharacter so it is automatically provided and it’s possible to
create objects of Hero.
Feedback

In class Adventure, you can see that there are four methods that take as
arguments the various interfaces and the concrete class. When a Hero
object is created, it can be passed to any of these methods, which means it
is being upcast to each interface in turn. Because of the way interfaces
are designed in Java, this works without any particular effort on the part
of the programmer.
Feedback

Keep in mind that the core reason for interfaces is shown in the above
example: to be able to upcast to more than one base type. However, a
second reason for using interfaces is the same as using an abstract base
class: to prevent the client programmer from making an object of this
class and to establish that it is only an interface. This brings up a
question: Should you use an interface or an abstract class? An
interface gives you the benefits of an abstract class and the benefits of
an interface, so if it’s possible to create your base class without any
method definitions or member variables you should always prefer
interfaces to abstract classes. In fact, if you know something is going to
be a base class, your first choice should be to make it an interface, and
only if you’re forced to have method definitions or member variables

should you change to an abstract class, or if necessary a concrete class.
Feedback

Name collisions when combining interfaces
You can encounter a small pitfall when implementing multiple interfaces.
In the above example, both CanFight and ActionCharacter have an

Chapter 8: Interfaces & Inner Classes 343
identical void fight( ) method. This is not a problem, because the
method is identical in both cases, but what if it isn’t? Here’s an example:
//: c08:InterfaceCollision.java

interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }

class C2 implements I1, I2 {
public void f() {}
public int f(int i) { return 1; } // overloaded
}

class C3 extends C implements I2 {
public int f(int i) { return 1; } // overloaded
}

class C4 extends C implements I3 {
// Identical, no problem:
public int f() { return 1; }
}


// Methods differ only by return type:
//! class C5 extends C implements I1 {}
//! interface I4 extends I1, I3 {} ///:~

The difficulty occurs because overriding, implementation, and
overloading get unpleasantly mixed together, and overloaded methods
cannot differ only by return type. When the last two lines are
uncommented, the error messages say it all:
InterfaceCollision.java:23: f( ) in C cannot implement f( ) in I1;
attempting to use incompatible return type
found : int
required: void
InterfaceCollision.java:24: interfaces I3 and I1 are incompatible; both
define f( ), but with different return type
Using the same method names in different interfaces that are intended to
be combined generally causes confusion in the readability of the code, as
well. Strive to avoid it.
Feedback


344 Thinking in Java www.BruceEckel.com
Extending an interface
with inheritance
You can easily add new method declarations to an interface using
inheritance, and you can also combine several interfaces into a new
interface with inheritance. In both cases you get a new interface, as
seen in this example:
//: c08:HorrorShow.java
// Extending an interface with inheritance.


interface Monster {
void menace();
}

interface DangerousMonster extends Monster {
void destroy();
}

interface Lethal {
void kill();
}

class DragonZilla implements DangerousMonster {
public void menace() {}
public void destroy() {}
}

interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}

class VeryBadVampire implements Vampire {
public void menace() {}
public void destroy() {}
public void kill() {}
public void drinkBlood() {}
}

public class HorrorShow {

static void u(Monster b) { b.menace(); }
static void v(DangerousMonster d) {
d.menace();

Chapter 8: Interfaces & Inner Classes 345
d.destroy();
}
static void w(Lethal l) { l.kill(); }
public static void main(String[] args) {
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
} ///:~

DangerousMonster is a simple extension to Monster that produces a
new interface. This is implemented in DragonZilla.
Feedback

The syntax used in Vampire works only when inheriting interfaces.
Normally, you can use extends with only a single class, but since an
interface can be made from multiple other interfaces, extends can refer
to multiple base interfaces when building a new interface. As you can
see, the interface names are simply separated with commas.
Feedback


Grouping constants
Because any fields you put into an interface are automatically static and
final, the interface is a convenient tool for creating groups of constant
values, much as you would with an enum in C or C++. For example:
//: c08:Months.java
// Using interfaces to create groups of constants.
package c08;

public interface Months {
int
JANUARY = 1, FEBRUARY = 2, MARCH = 3,
APRIL = 4, MAY = 5, JUNE = 6, JULY = 7,
AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10,
NOVEMBER = 11, DECEMBER = 12;
} ///:~

Notice the Java style of using all uppercase letters (with underscores to
separate multiple words in a single identifier) for static finals that have
constant initializers.
Feedback


346 Thinking in Java www.BruceEckel.com
The fields in an interface are automatically public, so it’s unnecessary
to specify that.
Feedback

You can use the constants from outside the package by importing c08.*
or c08.Months just as you would with any other package, and
referencing the values with expressions like Months.JANUARY. Of

course, what you get is just an int, so there isn’t the extra type safety that
C++’s enum has, but this (commonly used) technique is certainly an
improvement over hard-coding numbers into your programs. (That
approach is often referred to as using “magic numbers” and it produces
very difficult-to-maintain code.)
Feedback

If you do want extra type safety, you can build a class like this
1
:
//: c08:Month.java
// A more robust enumeration system.
package c08;
import com.bruceeckel.simpletest.*;

public final class Month {
private static Test monitor = new Test();
private String name;
private static int counter = 1;
private int order = counter++;
private Month(String nm) { name = nm; }
public String toString() { return name; }
public final static Month
JAN = new Month("January"),
FEB = new Month("February"),
MAR = new Month("March"),
APR = new Month("April"),
MAY = new Month("May"),
JUN = new Month("June"),
JUL = new Month("July"),

AUG = new Month("August"),
SEP = new Month("September"),
OCT = new Month("October"),
NOV = new Month("November"),

1
This approach was inspired by an e-mail from Rich Hoffarth. Item 21 in Joshua Bloch’s
Effective Java (Addison-Wesley, 2001) covers the topic in much more detail.

Chapter 8: Interfaces & Inner Classes 347
DEC = new Month("December");
public final static Month[] month = {
JAN, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC
};
public final static Month number(int ord) {
return month[ord - 1];
}
public static void main(String[] args) {
Month m = Month.JAN;
System.out.println(m);
m = Month.number(12);
System.out.println(m);
System.out.println(m == Month.DEC);
System.out.println(m.equals(Month.DEC));
System.out.println(Month.month[3]);
monitor.expect(new String[] {
"January",
"December",
"true",

"true",
"April"
});
}
} ///:~

Month is a final class with a private constructor so no one can inherit
from it or make any instances of it. The only instances are the final static
ones created in the class itself: JAN, FEB, MAR, etc. These objects are
also used in the array month, which lets you iterate through an array of
Month2 bjects. The number( ) method allows you to select a Month by
giving its corresponding month number. In main( ) you can see the type
safety: m is a Month object so it can be assigned only to a Month. The
previous example Months.java provided only int values, so an int
variable intended to represent a month could actually be given any integer
value, which wasn’t very safe.
Feedback

This approach also allows you to use == or equals( ) interchangeably, as
shown at the end of main( ). This works because there can be only one
instance of each value of Month. In Chapter 11 you’ll learn about another
way to set up classes so the objects can be compared to each other.
Feedback
There’s also a month field in java.util.Calendar.
Feedback

348 Thinking in Java www.BruceEckel.com
Apache’s “Jakarta Commons” project contains tools to create
enumerations similar to the above, but with less effort. See
jakarta.apache.org/commons, under “lang,” in the package

org.apache.commons.lang.enum. This project also has many other
potentially useful libraries.
Feedback

Initializing fields in interfaces
Fields defined in interfaces are automatically static and final. These
cannot be “blank finals,” but they can be initialized with nonconstant
expressions. For example:
//: c08:RandVals.java
// Initializing interface fields with
// non-constant initializers.
import java.util.*;

public interface RandVals {
Random rand = new Random();
int randomInt = rand.nextInt(10);
long randomLong = rand.nextLong() * 10;
float randomFloat = rand.nextLong() * 10;
double randomDouble = rand.nextDouble() * 10;
} ///:~

Since the fields are static, they are initialized when the class is first
loaded, which happens when any of the fields are accessed for the first
time. Here’s a simple test:
Feedback

//: c08:TestRandVals.java
import com.bruceeckel.simpletest.*;

public class TestRandVals {

private static Test monitor = new Test();
public static void main(String[] args) {
System.out.println(RandVals.randomInt);
System.out.println(RandVals.randomLong);
System.out.println(RandVals.randomFloat);
System.out.println(RandVals.randomDouble);
monitor.expect(new String[] {
"%% -?\\d+",
"%% -?\\d+",
"%% -?\\d\\.\\d+E?-?\\d+",

Chapter 8: Interfaces & Inner Classes 349
"%% -?\\d\\.\\d+E?-?\\d+"
});
}
} ///:~

The fields, of course, are not part of the interface but instead are stored in
the static storage area for that interface.
Feedback

Nesting interfaces
Interfaces may be nested within classes and within other interfaces
2
. This
reveals a number of very interesting features:
//: c08:nesting:NestingInterfaces.java
package c08.nesting;

class A {

interface B {
void f();
}
public class BImp implements B {
public void f() {}
}
private class BImp2 implements B {
public void f() {}
}
public interface C {
void f();
}
class CImp implements C {
public void f() {}
}
private class CImp2 implements C {
public void f() {}
}
private interface D {
void f();
}
private class DImp implements D {
public void f() {}
}

2
Thanks to Martin Danner for asking this question during a seminar.

350 Thinking in Java www.BruceEckel.com
public class DImp2 implements D {

public void f() {}
}
public D getD() { return new DImp2(); }
private D dRef;
public void receiveD(D d) {
dRef = d;
dRef.f();
}
}

interface E {
interface G {
void f();
}
// Redundant "public":
public interface H {
void f();
}
void g();
// Cannot be private within an interface:
//! private interface I {}
}

public class NestingInterfaces {
public class BImp implements A.B {
public void f() {}
}
class CImp implements A.C {
public void f() {}
}

// Cannot implement a private interface except
// within that interface's defining class:
//! class DImp implements A.D {
//! public void f() {}
//! }
class EImp implements E {
public void g() {}
}
class EGImp implements E.G {
public void f() {}
}
class EImp2 implements E {
public void g() {}

Chapter 8: Interfaces & Inner Classes 351
class EG implements E.G {
public void f() {}
}
}
public static void main(String[] args) {
A a = new A();
// Can't access A.D:
//! A.D ad = a.getD();
// Doesn't return anything but A.D:
//! A.DImp2 di2 = a.getD();
// Cannot access a member of the interface:
//! a.getD().f();
// Only another A can do anything with getD():
A a2 = new A();
a2.receiveD(a.getD());

}
} ///:~

The syntax for nesting an interface within a class is reasonably obvious,
and just like non-nested interfaces these can have public or package-
access visibility. You can also see that both public and package-access
nested interfaces can be implemented as public, package-access, and
private nested classes.
Feedback

As a new twist, interfaces can also be private, as seen in A.D (the same
qualification syntax is used for nested interfaces as for nested classes).
What good is a private nested interface? You might guess that it can only
be implemented as a private inner class as in DImp, but A.DImp2
shows that it can also be implemented as a public class. However,
A.DImp2 can only be used as itself. You are not allowed to mention the
fact that it implements the private interface, so implementing a private
interface is a way to force the definition of the methods in that interface
without adding any type information (that is, without allowing any
upcasting).
Feedback

The method getD( ) produces a further quandary concerning the private
interface: it’s a public method that returns a reference to a private
interface. What can you do with the return value of this method? In
main( ), you can see several attempts to use the return value, all of which
fail. The only thing that works is if the return value is handed to an object
that has permission to use it—in this case, another A, via the receiveD( )
method.
Feedback


×