Our Lamp
program is now quite respectably complex,
but it's still more of a sketch of a lamp than a real lamp.
The main problem now is how to model different types of lamps
(desk, floor, ceiling, wall, portable, articulated,
strobe, incandescent, fluorescent, ultraviolet, sun).
This leads to a whole new scale of problem.
The type of a lamp can completely alter both its state and behavior.
To model that kind of radical change
we need some way to switch between whole banks of variables and methods---we
need some way to switch between different types.
We saw a little of the problem
when we added a simple dimmer switch to our lamp model.
That one change forced a lot of change in the class.
Really, we should no longer call it class Lamp
at all
but rather class DimmerControlledLamp
.
The way we have it now,
all lamps must be dimmer-controlled
since all of them can respond to requests to
brighten()
or darken()
.
We no longer have such a thing as a plain old lamp.
How can we have both?
How can we have any type of lamp we choose?
Even with all the complexifying mechanisms we've seen so far,
we've only scratched the surface of what's possible in Java,
which in turn only scratches the surface of the universe's complexity.
Extending class Lamp
The universe has many complex types of things;
but many of those types are related to each other.
It's silly to try to model each one independently.
Instead we can model one, then try to describe another in terms of the first,
and so claw our way out of the complexity jungle.
So instead of one monster class Lamp
beginning as follows:
package lamps;
public class Lamp
{
/*
Define a lamp that can turn on and off,
report its state, report its state changes,
have a wattage, brighten and darken in stages,
compare its brightness with another lamp,
have and report a name, and make sure it's off on creation.
The class itself remembers the maximum possible wattage
of any lamp and the default wattage of each lamp.
*/
we could have two classes in package
lamps
:
One, representing the traditional plain old vanilla lamp,
beginning as follows:
package lamps;
public class Lamp
{
/*
Define a lamp that can turn on and off,
report its state, report its state changes,
have a wattage, have and report a name,
and make sure it's off on creation.
The class itself remembers the maximum possible wattage
of any lamp and the default wattage of each lamp.
*/
And another, representing lamps with dimmer switches, beginning as follows:
package lamps;
import lamps.Lamp;
public class DimmerControlledLamp extends Lamp
{
/*
Define a type of Lamp that can brighten and darken
in stages, and compare its brightness with another lamp.
*/
Class Lamp
will have the state and behavior we developed for
plain old lamps.
It will have a lampIsOn
variable, a name
variable,
and a wattage
variable.
It won't have a brightness
variable,
since plain old lamps can only have a fixed brightness if they're on.
It will also support the turnOn()
, turnOff()
,
setName()
, and getName()
methods.
Finally, it will have a class variable (a static
variable)
to hold every lamp's MAXIMUM_WATTAGE
,
and another to hold every lamp's DEFAULT_WATTAGE
.
Class DimmerControlledLamp
, on the other hand,
only needs a brightness
variable
and three methods to report, alter, and compare its brightness:
getBrightness()
,
setBrightness()
, and
isBrighterThan()
.
We connect the two with the word extends
in the definition of class DimmerControlledLamp
, as follows:
public class DimmerControlledLamp extends Lamp
Using an Extended Class
Once we create a DimmerControlledLamp
object,
how do we get it to do all the Lamp
things it's supposed to?
We would like to say:
DimmerControlledLamp lamp;
lamp = new DimmerControlledLamp();
lamp.turnOn();
lamp.turnOff();
just as if the new lamp,
besides letting us use a dimmer switch,
also let us turn it on and off like a normal non-dimmer-controlled lamp.
Well, we can.
Although the new lamp is an object of class DimmerControlledLamp
,
it's also an object of class Lamp
(since DimmerControlledLamp
extends
Lamp
),
so it can respond to all the normal Lamp
requests
defined in class Lamp
.
It inherits all those methods.
It, however, doesn't inherit a name
variable,
a lampIsOn
variable, or a wattage
variable
since we defined those as private
in class Lamp
.
Only objects of class Lamp
get copies of those three variables.
We can treat the new DimmerControlledLamp
object
as if it were a Lamp
object
or as if it were a DimmerControlledLamp
object.
All the methods we previously defined only for Lamp
objects
are still available to us, as are the new methods we define in class
DimmerControlledLamp
.
If, however, we create a plain old Lamp
object
instead of a DimmerControlledLamp
, like so:
Lamp lamp;
lamp = new Lamp();
we can expect it to do all the Lamp
things
we know Lamp
objects can do,
but we can't ask it to do DimmerControlledLamp
things;
it would have no idea how to do them.
So the following is illegal:
Lamp lamp;
lamp = new Lamp();
lamp.turnOn();
lamp.brighten(); //error: normal lamps don't know how
The
super
Reference
There is one fly in this particular happy stew. Suppose we did the following:
DimmerControlledLamp lamp;
lamp = new DimmerControlledLamp(100);
expecting to get a 100-watt lamp that also has a dimmer switch.
We would instead get a stern talking to from the Java interpreter.
It would complain that there is no constructor
that takes an int
parameter
in class DimmerControlledLamp
.
The problem is this: we are here asking it to create a
DimmerControlledLamp
object, and it's perfectly happy to do that.
Unfortunately, we're also asking it to then use the constructor that takes
one int
parameter defined in class Lamp
to initialize that object's wattage
to 100 watts.
This it won't do.
Constructors, since they are named exactly the same as their class, can't be used directly by objects of other classes, even ones that extend the class. Constructors aren't methods---the Java interpreter executes one of them exactly once, and only when it creates the object. So if we need special initialization for objects of an extended class, we have to write new constructors.
Fortunately, we can refer to a class and its extended classes
as a superclass and its subclasses.
Using
So here's a constructor that takes a
The strange statement "
This
Every class has to have at least one constructor
to initialize its objects.
This brings up a puzzle:
when we were writing
The answer is that it didn't.
Whenever we don't write any constructor for a class
the Java interpreter secretly writes one for us.
The secret constructor it writes is very simple though---it
simply executes the zero-parameter constructor
for the superclass portion of the object.
Here, for instance, is the secret constructor for
The Java interpreter always adds such a zero-parameter constructor
to every class that we neglect to write a constructor for.
This secret constructor simply requests execution of
the object's superclass' zero-parameter constructor,
whether one exists or not.
If the superclass has such a constructor,
this subclass request ensures that every object can create and initialize
objects of each class we write
unless we specifically disallow that (we'll find out how later).
The secret constructor for a class has the same access level
as the class itself.
So the secret constructor for
If we write even one constructor for a class,
the Java interpreter won't add the secret zero-parameter one---it
assumes that we have covered all the different ways to initialize
objects of that class ourselves.
This can lead to an obscure error.
Here's the problem:
suppose we add
to class
This code worked just fine before we added any constructor at all,
since the Java interpreter secretly added
a zero-parameter constructor to class
The Java interpreter is using the same assumption
that it uses when it secretly prefixes
So when we try to execute the above innocent-seeming object creation request
the Java interpreter complains
because class
Further, the Java interpreter will complain
even though class
So if we ever need to initialize
Every class that does not explicitly extend another class
always
Class
Although class
In theater terms, class
Since class
Every class (except class
Here are the five Java secrets we've uncovered so far.
The Java interpreter secretly:
Suppose we want
A natural name for the new smooth on method is
To avoid a method name conflict we could do several things.
For instance, we could name the new method differently,
say,
Neither of those two ideas are good.
Either way, we would then have no general way
to
It would be nice to tell either type of lamp to
simply
We get exactly that behavior if we name the new
Now there are two
When we create a
We must also override class
While the idea of overriding method
Using the one-parameter
Each object should be in sole control of its own state,
and each variable should only have unique access methods.
That's still true even for objects of subclasses accessing variables
or executing methods of their superclass.
We have to be ever vigilant about keeping our objects encapsulated
and not let easy ways of coding seduce us into style crime.
We overload a method
when we add more than one method, each with different signatures,
to a single class.
We override a method in a class when we extend the class and create
another method with exactly the same signature in the subclass.
The subclass method overrides the superclass method.
For example, in the
While we can override and overload methods as much as we want,
and we can overload constructors as much as we want,
we can't override constructors,
since a subclass does not inherit its superclass' constructors.
We'll find out why later.
In brief, overloading adds behavior to a class,
overriding replaces behavior in a subclass.
Method overloading and overriding
add yet more layers of code complexification on top
of the schemes we're already familiar with:
Let's now override a method from class
This new
Here's how the above
Let's break this down.
First we create a new
Then we ask that
Then we ask that new
Finally, we ask the
We shorten that whole long series of requests to this:
Class
Each type of actor should at least fill in some particulars
and say, for instance, "I am a Lead Actor", or "I am a Walk-On",
or "I am a Butler".
Further, it would be nice to distinguish between
various actors all playing the same role,
so that actors could say, for example:
"I am a Butler and my name is Jeeves".
Our hypothetical class
Once every class has a
The
The
What looks like one very clever
Both overloading and overriding
help objects be smarter about what they should do
without us having to always specify every last detail of what they should do.
The smarter our objects are,
the easier it is for us to combine them to do more complex things.
We're not finished with
This is odd, though,
because the
Here again is the
This method clearly needs the
To summarize, we're asking a
Our current
In class
This is true even though objects of class
Fortunately, we can get around this because class
Now, here's the trick:
a
So here's a version of
Now, when given a reference variable pointing to a
We now have three versions of
By declaring a class' variables as
So we could solve our last problem of gaining access
to the
Unfortunately,
It seems so much easier to declare the
We fell into the previous problem not so much because
class
Forcing unique access to the
The problem isn't serious when we have only one variable
and only one extended class,
but imagine how hard it is to tell just where, when, and how we're changing
any one of dozens of
The Java interpreter only makes this problem
worse by also allowing objects of any class in the same package
to access
It's much safer to keep all non-
In sum, although
Consequently, although we'll still keep
all our non-
So far we've been going from class
An interface is a bit like a class except that we never specify any behavior
for the methods, we simply state their signatures.
First we define an interface that all light sources must implement,
then we can implement the interface
(that is, define the behavior of the interface's declared methods)
in lots of different classes.
None of those classes need to extend another.
We state that we're simply declaring a method
but not specifying its implementation
with the word
We could then say, for example:
From then on we could treat both lamps and fireflies
as if they were objects of classes that extended some mythical
Interfaces help us make elegant programs.
Suppose, for instance, we're building a display with a number of light sources
on the screen.
We might have fireflies flying around and turning on and off as they do so,
fixed lamps of various fixed brightness, dimmer-controlled lamps, the sun
rotating above, a bonfire on a beach, flashlights, flints, and so forth.
The behavior of each such object
might be defined in a completely different class,
some (or all) of which we may or may not have written,
and some (or all) of which we may have no control over.
Once each of those classes implement the
Here's some sample code:
Their uniform interface let's use completely ignore
how each of the classes implements those two methods.
We don't have to care that fireflies can
We name interfaces the same way we name classes---by concatenating a
collection of capitalized words that describe the interface.
It's good style, however, to make interface names adjectives, or
adjectival phrases, and to make class names nouns or noun phrases.
That helps us distinguish between them
while keeping the uniform structure of their names.
Interfaces are elegant, but they present us with a serious dilemma.
What exactly are they?
So far, whenever we've declared a certain thing, whether it's a variable,
a class, or a method,
we've also defined it (that is, given instructions on how it should act).
Interfaces, though, are pure declaration; there is no definition at all.
The key to the puzzle is this:
A class is really two related things rolled into one.
The first is its factoryness:
it can direct the production of objects.
The second is its contractness:
it guarantees that those objects will know how to do certain things.
These two aspects of classes are related, but not the same.
We can imagine having a contract with no factory attached,
but we can't have a factory with no contract attached.
Even an empty factory
(a class with no methods or variables, or a package with no classes)
has a contract:
it's promising to do absolutely nothing.
A contract with no factory attached, however, is imaginable.
In Java, it's an interface.
We can extend interfaces to produce new interfaces
just as we can extend classes to produce new classes.
Say, for instance we wanted to declare an interface
We could instead have an interface
Any class implementing
Although interfaces can extend from each other,
and classes can extend from each other,
classes cannot extend from interfaces.
They can, however, implement them.
Also, although interfaces can extend from multiple other interfaces,
classes can only extend from one other class.
Classes, however, can implement multiple interfaces.
An interface is purely
In theater terms, an abstract class is like a draft of a screenplay.
Some parts of the screenplay may be fleshed out
while others may still be vague,
except that we know that there must be a huge explosion (or a great kiss)
at the end.
We can leave all the particulars to other writers to complete.
For example, suppose we were writing a collection of classes to model an
orchestra.
We might have a
There is no single instrument that all musicians play,
so we can't implement the
If class
The answer is that there is then no guarantee that a subclass of class
We can declare a class to be
Each way of expanding the idea of class
leads directly to the idea of type.
When we define a class we also define a type;
it's just that we also define a way for the class to implement the type
(that is, construct objects having that type).
When we define an abstract class we again define a type;
this time though, all or some (or none) of the methods in the class
might be
An interface is a contract
that all classes implementing the interface promise to abide by.
And that, in essence, is what a type is.
It's something that specifies how things should behave,
not necessarily what they have to do to behave that way.
It's now easy to see how to create complex Java programs:
we merely decide what the behavior should be
of each type of thing we need in the program.
Then we declare interfaces, or abstract classes, or concrete classes, to
describe those expected behaviors.
Then we link related ones with
Of course, as we shall learn, nothing worthwhile is ever that simple.
Here is the entire program, including everything we've learned so far:
super
in a subclass
lets an object refer to the superclass portion of itself,
just as using this
lets an object refer to itself.
wattage
as a parameter for
class DimmerControlledLamp
:
public DimmerControlledLamp(int wattage)
{
/*
Initialize this lamp's superclass portion,
Then set this lamp's initial brightness.
*/
super(wattage);
this.setBrightness(0);
}
super(wattage)
"
asks the Java interpreter to execute the following constructor
in the superclass, class Lamp
:
public Lamp(int wattage)
{
/*
Set this lamp's initial state
as being in the off position.
Set this lamp's wattage to the given wattage.
Do not let any lamp have a wattage higher than
Lamp.MAXIMUM_WATTAGE.
Give this lamp a generic name.
*/
lampIsOn = false;
if (wattage > Lamp.MAXIMUM_WATTAGE)
this.wattage = Lamp.MAXIMUM_WATTAGE;
else
this.wattage = wattage;
name = "Lamp";
}
Lamp
constructor,
which takes one int
parameter,
is in the Lamp
class
portion of the newly created object.
That initializes the Lamp
portion of the new
DimmerControlledLamp
object properly.
After that, we also have to initialize the
DimmerControlledLamp
object's brightness
variable,
and that completes the initialization of the whole object.
The Secret Constructor
FredsScript
,
and again when we first started writing Lamp
,
we had no constructor of any kind.
So if every class has to have at least one constructor
why then did the Java interpreter
let us get away without writing constructors for those classes?
FredsScript
:
And here is the secret constructor for
FredsScript()
{
super();
}
Lamp
:
public Lamp()
{
super();
}
FredsScript
has default, or
package, access because its class, FredsScript
, has
default access.
On the other hand,
the secret constructor for Lamp
has public
access because its class, Lamp
, has public
access.
A Confusing Error
DimmerControlledLamp
the above one-parameter constructor
(which takes a wattage
parameter),
but we don't add any other constructors.
Now when we try to do the following:
the Java interpreter complains.
DimmerControlledLamp lamp;
lamp = new DimmerControlledLamp();
DimmerControlledLamp
for us.
But when we add even one constructor,
the Java interpreter (foolishly) assumes that we know what we're doing
and doesn't add the zero-parameter constructor it would have added
if we hadn't bothered to add any constructor at all.
this
to method execution requests or variable accesses.
It only does so when we don't state a specific object
to execute the method or to access the variable.
If we state an object to do the work,
the Java interpreter doesn't try to second-guess us;
it assumes that we know what we're doing.
The same goes for constructor execution requests.
DimmerControlledLamp
doesn't have a zero-parameter constructor for the Java interpreter to execute
after creating a new DimmerControlledLamp
object,
which is what we're asking it to do when we say:
lamp = new DimmerControlledLamp();
Lamp
,
which class DimmerControlledLamp
extends
,
already has a zero-parameter constructor.
The Java interpreter does its best, but it isn't very bright.
DimmerControlledLamp
objects with a zero-parameter constructor,
we need to add one just like the secret constructor to class
DimmerControlledLamp
.
It's good style to always add such a constructor to every class we write,
since that avoids the above problem entirely.
Class Object
extends
class Object
.
The Java interpreter secretly adds the words "extends Object
"
to every class definition that does not already extend some class.
So even though we say:
class
public class Lamp
Lamp
actually begins this way:
and we can write it that way if we want.
Declaring class
public class Lamp extends Object
Lamp
that way
makes it clear that it extends
class Object
.
DimmerControlledLamp
, on the other hand,
will always appear as:
since it already
public class DimmerControlledLamp extends Lamp
extends
a class.
The Java interpreter won't let us write a class that extends
two or more classes.
DimmerControlledLamp
doesn't extend class Object
explicitly,
it still extends
class Object
through class Lamp
,
since class Lamp
secretly extends
class Object
.
All classes extend class Object
one way or the other.
Object
is like a generic class
Actor
that all classes extend.
If such a thing existed,
class Actor
would specify all the things every actor must do.
In the theater world, that might be things like being able to find the stage,
knowing how to hold a prop, knowing how to speak, and so on.
In the Java world, class Object
specifies things like
how to tell whether one object has the same variables and values for those
variables as another object
(the equals()
method)
and being able to report a String
representation of an object (the toString()
method).
DimmerControlledLamp
extends
class Lamp
and class Lamp
extends
class Object
,
every DimmerControlledLamp
object
is also an Object
object.
So it will know how to respond to requests to
equals()
or toString()
.
Object
) extends
some class.
That superclass may extend some other class, and so on,
but we must eventually arrive at
some class that (secretly) extends
class Object
.
So every class extends
class Object
one way or the other.
So all objects, no matter their class,
will know how to respond to
equals()
or toString()
.
Secret Summary
prefixes
There will be more.
this
to every method execution request
or variable access whose object we don't already specify.
creates an unnameable object associated with each class
to execute class methods and to hold and access class variables.
creates an unnamed package to hold all classes
whose package we don't already specify.
adds a zero-parameter constructor containing super()
,
with the same access level as its class,
to every class whose constructors we don't already specify.
extends
from class Object
every class whose superclass we don't already specify.
Overriding Methods
DimmerControlledLamp
objects to
turn on smoothly from zero brightness to some maximum initial brightness,
which is, let's say, 3.
We should name that constant
to avoid littering our code with the number 3
then forgetting what it stands for.
Let's call it, say, INITIAL_BRIGHTNESS
.
It should of course be a class constant for class
DimmerControlledLamp
(that is, it should be static
and final
),
and, since it's a constant, it can be public
as well.
turnOn()
.
But we already have a method named turnOn()
in class
Lamp
.
Since class DimmerControlledLamp
extends
class Lamp
,
all DimmerControlledLamp
objects already know how to
turnOn()
.
turnOnSmoothly()
.
Or we could write another turnOn()
method,
this time in class DimmerControlledLamp
,
that takes some parameter (perhaps the initial brightness).
This time, the name is the same, but the signature is different.
turnOn()
either type of lamp
with one fixed method execution request.
We would always have to remember which type of lamp
we're trying to turnOn()
.
This is a lose.
turnOn()
and have the lamp itself
decide how it's going to turn on based on its own particular type.
Normal lamps would simply snap on, but dimmer-controlled lamps
would come on smoothly.
This would put the intelligence about how to turnOn()
where it
belongs---in the lamp.
DimmerControlledLamp
method turnOn()
and give it exactly the same signature as class
Lamp
's turnOn()
method:
public class DimmerControlledLamp extends Lamp
{
//maximum initial brightness for each dimmer-controlled lamp
public final static int INITIAL_BRIGHTNESS = 3;
//some code
public void turnOn()
{
/*
Turn this lamp on gradually until it is at
DimmerControlledLamp.INITIAL_BRIGHTNESS.
*/
this.setBrightness(1);
for (int index = 1;
index < DimmerControlledLamp.INITIAL_BRIGHTNESS;
index++)
{
brighten();
}
}
//some more code
}
turnOn()
methods,
both with the same signature;
one is in class Lamp
and the other is in class DimmerControlledLamp
.
DimmerControlledLamp
object
and ask it to turnOn()
,
it will execute the turnOn()
method we just defined
in class DimmerControlledLamp
,
not the one we earlier defined in class Lamp
.
The DimmerControlledLamp
's turnOn()
method
overrides the Lamp
's turnOn()
method.
Lamp
's isOn()
and
reportState()
methods
in class DimmerControlledLamp
otherwise all methods in
DimmerControlledLamp
will use the versions of those two methods
it inherits from class Lamp
.
Those versions use the
lampIsOn
variable and the wattage
variable
instead of the brightness
variable.
Keeping Objects Encapsulated
turnOn()
is good,
the code itself is not.
Here's a better way to implement the new turnOn()
method:
public void turnOn()
{
/*
Turn this lamp on gradually until it is at
DimmerControlledLamp.INITIAL_BRIGHTNESS.
*/
this.setBrightness(1);
brighten(DimmerControlledLamp.INITIAL_BRIGHTNESS - 1);
}
brighten()
method
is better than using the zero-parameter brighten()
method,
since the one-parameter brighten()
already uses the zero-parameter brighten()
.
That ensures unique access
to the zero-parameter brighten()
method,
at least within class DimmerControlledLamp
,
and unique access helps ensure encapsulation
in case we ever need to change either class.
Method Overloading versus Method Overriding
turnOn()
method of class
DimmerControlledLamp
that overrides
the version of the turnOn()
method in class
Lamp
,
a DimmerControlledLamp
object
can execute either version of the two overloaded
brighten()
method we defined in class Lamp
since every such object inherits it from class Lamp
.
There are four methods in all---two
overloaded versions of brighten()
,
with different signatures, in class DimmerControlledLamp
,
and one version of turnOn()
in class Lamp
,
and another version, with the same signature,
in class DimmerControlledLamp
that overrides the one in class Lamp
.
if
, if-else
, while
, for
,
method execution requests, and return
statements.
Now we have two more ways to control statement execution order.
Overriding toString()
Object
in
class Lamp
.
Recall that every Lamp
has a String
name
variable holding a string representing its name.
We want to report that name in our toString()
method for class
Lamp
.
Here's one way to do it:
If a lamp's
public String toString()
{
/*
Report this lamp's type and name.
*/
return "Lamp[" + this.name + "]";
}
name
variable contains the string, say, "kandinsky",
then asking it to execute this method results in a String
object
containing the string "Lamp[kandinsky]".
toString()
method is not the same as class
Lamp
's getName()
method.
That method returns the string value of
a Lamp
object's String
name
variable.
The toString()
method, however,
returns a string representation of the name and type of the lamp.
Fun with String()
s
toString()
method works.
Because of all of the String
shortcuts the Java interpreter
allows us,
the simple-looking statement:
is short for the much longer statement:
return "Lamp[" + this.name + "]";
return ((new String("Lamp[")).concat(this.name)).
concat(new String("]"));
String
object
containing the string "Lamp[":
new String("Lamp[")
String
object to execute its
concat()
method with a String
parameter
that is the name
of this object
and produce another
String
object containing the two strings joined into one:
(new String("Lamp[")).concat(this.name)
String
object to
concat()
enate the string contents of a new String
object containing the string "]"
and produce yet another
String
object containing the two previous strings:
((new String("Lamp[")).
concat(this.name)).concat(new String("]"));
Lamp
object executing this
toString()
method to return
the
final String
object produced,
which contains a concatenation of all three original strings:
return ((new String("Lamp[")).concat(this.name)).
concat(new String("]"));
return "Lamp[" + this.name + "]";
Why Override toString()
?
Object
already has a toString()
method.
Why override it in class Lamp
?
The problem is that class Object
's toString()
is
generic.
It's like putting a method in our mythical class Actor
that says
"I am an Actor".
Big whoop.
Actor
can't know those particulars.
Similarly, class Object
can't either.
Each class should have its own toString()
to at least report the name of the role,
and---even better---some unique way to refer to
each particular actor playing that role.
toString()
then given any reference variable we can always do the following:
and print a string representation of the name of the particular object
that the reference variable points to.
Of course, that name is just a
System.out.println([reference variable].toString());
String
,
it's not the same as the actor's secret name, which we can never know.
Overloading println()
toString()
operation is so common that
we can shorten this:
to just this:
System.out.println([reference variable].toString());
System.out.println([reference variable]);
println()
method is smart enough to know that
if it's given just a reference variable
it should first ask the object that the variable names
to execute its toString()
method
(every object must have one, if only the
one derived from class Object
).
Then it prints the String
the object returns.
println()
method
is really only one of several overloaded versions,
each of which takes a different type of parameter
(that is, each has a different signature).
Because they are all overloaded,
we don't need to use a special name for each one of them,
we simply say println()
and it just works.
Using toString()
toString()
yet
because we have another class of objects to name, namely:
class DimmerControlledLamp
.
Now, since class DimmerControlledLamp
extends
class
Lamp
,
when given a DimmerControlledLamp
object
whose name
is "kandinsky" we can always say:
and execute the
System.out.println([reference variable]);
toString()
method that class
DimmerControlledLamp
inherited from class Lamp
.
(That toString()
overrides the
toString()
that class Lamp
itself
inherited from class Object
.)
So this will print the string "Lamp[kandinsky]".
String
variable name
is private
in class Lamp
.
Since private
variables and methods aren't passed on
from a class to its subclasses,
how could this possibly work if class
DimmerControlledLamp
didn't inherit a name
variable from class Lamp
?
toString()
method we added to class
Lamp
:
public String toString()
{
/*
Report this lamp's type and name.
*/
return "Lamp[" + this.name + "]";
}
name
variable.
Fortunately, that variable is accessible to the method
since both the variable and the method are in class Lamp
.
That's the method we're really executing here,
even though we're executing it with a DimmerControlledLamp
object.
All DimmerControlledLamp
objects
inherit the toString()
method from class Lamp
since it's declared as public
in class Lamp
.
DimmerControlledLamp
object
to execute a public
method.
That object is also a Lamp
object
since DimmerControlledLamp
extends
class Lamp
.
Any DimmerControlledLamp
objects executing that method
use the contents of the private
name
variable
of class Lamp
,
then return a String object containing the string contents of
the variable.
Overriding toString()
Again
toString()
method in class Lamp
might be enough for some purposes,
but if we want more specific information about
class DimmerControlledLamp
objects,
we should override toString()
yet again.
This time we're going to override the one in class Lamp
with yet another one in class DimmerControlledLamp
.
DimmerControlledLamp
we'd like to write:
as we did in class
public String toString()
{
/*
Report this lamp's type and name.
*/
return "DimmerControlledLamp[" + this.name + "]";
}
Lamp
,
but it doesn't work.
The problem now is that the name
variable
is private
to objects of class Lamp
.
Recall that private
means that the variable or method
is only available to objects of the same class.
Even though class
DimmerControlledLamp
extends class Lamp
,
the Java interpreter won't let us directly refer to
private
variables of class Lamp
in objects of class
DimmerControlledLamp
.
DimmerControlledLamp
can use the contents of the name
variable
while executing the public
toString()
they inherited from class Lamp
.
Simply being able to see the contents of variable name
is a courtesy that class Lamp
extends to any object
since it supports a public
getName()
method
to report it.
It's quite another thing for class Lamp
to let
non-Lamp
objects access the variable.
Any object that can access the variable can change its contents.
Only class Lamp
objects can access that variable.
Lamp
also has a public
getName()
method
that we can use to get a Lamp
object's name.
Even though name
is private
in class Lamp
,
objects of any class,
including class DimmerControlledLamp
,
can ask a Lamp
object to execute the method.
DimmerControlledLamp
object is also a Lamp
object,
so it can ask itself to execute its getName()
method,
since it inherited one from class Lamp
.
toString()
that works for class
DimmerControlledLamp
:
public String toString()
{
/*
Report this lamp's type and name.
*/
return "DimmerControlledLamp[" + this.getName() + "]";
}
DimmerControlledLamp
object whose String
name
is "kandinsky"
and we say:
it will print "DimmerControlledLamp[kandinsky]".
System.out.println([reference variable]);
toString()
.
One in class Object
,
one in class Lamp
, and
one in class DimmerControlledLamp
.
Each one overrides the last.
Using protected
Variables and Methods
protected
we let objects of the class' subclasses access those variables.
Similarly, objects of a class' subclasses
can execute the class' protected
methods.
private
name
variable
in class Lamp
by changing its
access modifer in class Lamp
from private
to protected
.
Then we could directly access it in a DimmerControlledLamp
object.
protected
also means that objects of classes
in the same package as the given class also have access.
So an object's protected
variables (or its methods)
can be accessed directly by other objects of the class,
by objects of subclasses of the class,
or by objects of any class in the same package as the class.
That's too much accessibility.
Modifier
Classes
Methods and Variables
private
-undefined-
objects from same class
-none-
[=package]
objects from same package
objects from same class
or
objects from same package
protected
-undefined-
objects from same class
or
objects from same package or
objects from a subclass
public
all objects
all objects
Back To Encapsulation
name
variable of class
Lamp
as protected
and not have to bother with
accessor methods like setName()
and getName()
,
but that would lead us directly into style crime.
We would have broken the encapsulation of class Lamp
.
Lamp
's name
variable was private
but because we were seduced by our too casual use of the name
variable inside class Lamp
in the first place.
Using a naked name
variable inside class Lamp
is a style crime
because we already have a setName()
and
getName()
method in that class.
We should have been using those instead
since that forces unique access to the variable.
name
variable
even in class Lamp
would have made it much easier to see how to create another
toString()
in class
DimmerControlledLamp()
since it too would have access to
getName()
(but not name
)
since getName()
is public
while
name
is private
.
protected
variables
when we can access any of them from any subclass
going down, say, ten levels deep.
protected
variables.
final
variables
private
and then add public
or
protected
accessor methods to those that either every object
(for the public
methods)
or every descendant or packaged object
(for the protected
methods)
must have access to.
protected
gives more, ah, protection to
variables than simply making them public
,
all non-final
variables
should still be declared private
.
We can, however, now modify the rule for methods.
Instead of saying that all methods for external use
should be public
,
we could distinguish between those
everyone can use (which must be public
),
those solely for objects of subclasses,
or objects of classes in the same package
(protected
methods),
and those intended solely for objects of the class itself
(private
methods),
final
variables private
,
we might make class DimmerControlledLamp
's
private
setBrightness()
and
getBrightness()
methods protected
instead.
Interfaces
Lamp
to more specialized types of lamps,
what though about going from class Lamp
to more general light sources?
Suppose we wanted to add classes to describe matches,
and cigarette lighters, and candles, and flashlights, and bonfires,
and fireflies?
Most of these don't have a wattage
, a name
,
or anything like a dimmer switch,
but they all can give light, as lamps do.
To handle that the Java interpreter gives us interfaces.
abstract
(as opposed to concrete).
Here then is an interface that all Lamp
objects could implement:
Every method in an interface must be
public interface Luminous
{
public abstract void turnOn();
public abstract void turnOff();
}
public
and abstract
,
although if we leave those off
the Java interpreter will secretly add them for us.
We need the semicolons after each method name
because we're not defining the methods
(that is, we're not specifying how to implement them)
we're simply declaring methods of that particular signature.
It's up any classes implementing the interface
to specify complete methods with those signatures.
We could also say:
import Luminous;
public class FireFly implements Luminous
{
//code to implement the turnOn() and turnOff() methods
//declared in the Luminous interface
//plus code to do other FireFly things (like fly())
}
import Luminous;
public class Lamp implements Luminous
{
//code to implement the turnOn() and turnOff() methods
//declared in the Luminous interface
//plus code to do other Lamp things (like setName())
}
Luminous
class.
Using Interfaces
Luminous
interface,
though, we can hide all of that messiness behind the interface.
In our display program we can create a set of Luminous
objects from any classes that implement the interface,
then ask them to turnOn()
and turnOff()
as we wish.
//create an object to hold 4 Luminous reference variables
Luminous[] lights;
lights = new Luminous[4];
//create 4 Luminous objects
lights[0] = new FireFly();
lights[1] = new Lamp();
lights[2] = new DimmerControlledLamp();
lights[3] = new Bonfire();
//tell each of them to turnOn() and turnOff()
for (int index = 0; index < lights.length; index++)
{
lights[index].turnOn();
lights[index].turnOff();
}
fly()
or that cigarette lighters can burnOut()
.
All that matters is that they can both turnOn()
and
turnOff()
---they
both implement the Luminous
interface.
Contracts and Factories
Extending Interfaces
Heroic
.
public interface Heroic
{
public abstract void fly();
public abstract void swim();
public abstract void overAct();
}
CanSwim
and an interface
public interface CanSwim
{
public abstract void swim();
}
CanFly
and have
public interface CanFly
{
public abstract void fly();
}
Heroic
extend from CanSwim
and CanFly
:
Unlike classes, interfaces can extend from multiple other interfaces.
When extending from multiple interfaces we use commas to separate the
superinterface names.
public interface Heroic extends CanSwim, CanFly
{
public abstract void overAct();
}
Heroic
would have to define methods to
fly()
, overAct()
, and swim()
.
Interface Heroic
extends
from interface
CanSwim
,
and interface CanFly
,
so being able to fly()
and swim()
is part of the contract of all Heroic
objects.
They all promise to know how to do those things.
Abstract Classes
abstract
since we can't define any implementation for the behavior we're requiring.
A class---at least all the ones we've seen so far---is the opposite,
it's completely concrete.
We can also have classes that are inbetween;
these are abstract classes.
Musician
class, a Violist
class,
a Violinist
class, a Pianist
class, and so on.
When writing the Musician
class we'd like to specify all
the things that professional musicians do: getting paid,
rehearsing, renting a costume, starving, and so on.
Above all, though, we would want to specify that
they can play an instrument.
This, however, presents a problem.
play()
method
when writing class Musician
.
Only each subclass of Musician
, for example,
Violist
, can do that.
So we should declare the play()
method in class
Musician
as abstract
.
The class as a whole would then itself be abstract
.
We can't create a generic musician;
we can, however, create violists, or pianists,
and so forth.
Each of them would be special kinds of musicians.
Musician
can't implement its play()
method
why bother to have it in class Musician
at all?
Why not have each subclass implement a play()
method,
just as we must do if class Musician
weren't abstract?
Musician
will definitely have a play()
method.
It may, or it may not.
We can't tell for sure until we look at the class.
If, however, we declare play()
in class Musician
,
and if we declare it as abstract
,
then every client class is guaranteed that each non-abstract
subclass of class Musician
must have implemented a play()
method.
It's part of the contractual obligations of every
Musician
object.
abstract
yet still implement all its
methods.
That is, a class can be abstract
without any of its methods being
abstract
.
That's useful when we don't want objects to be able to create objects of the
class, even though they might be able to create objects of subclasses
of the class.
Reverting To Type
abstract
;
in other words, we needn't bother to explain how to do everything.
When we define an interface we are declaring a pure type;
we add no hints whatsoever on how it should be implemented.
extends
or implements
to cement the relations between them.
That avoids us having to describe each one in massive detail,
instead only describing each one in terms of their relation to
others we have already defined.
We keep on this way until everything is realizable as an object of a concrete
class somewhere.
At that point, we're done.
The Whole Program---Again
package lights;
public interface Luminous
{
public abstract void turnOn();
public abstract void turnOff();
}
package lights;
import lights.Luminous;
public class Lamp extends Object implements Luminous
{
/*
Define a lamp that can turn on and off,
report its state, report its state changes,
have a wattage, have and report a name,
and make sure it's off on creation.
The class itself remembers the maximum possible wattage
of any lamp and the default wattage of each lamp.
*/
//this lamp is either on or off
private boolean lampIsOn;
//this lamp may have a name
private String name;
//this lamp has a wattage
private int wattage;
//all lamps have a maximum possible wattage
public final static int MAXIMUM_WATTAGE = 500;
//all lamps have a default wattage
public final static int DEFAULT_WATTAGE = 60;
public Lamp()
{
/*
Set this lamp's initial state
as being a lamp.DEFAULT_WATTAGE-watt lamp
in the off position.
Give this lamp a generic name.
*/
lampIsOn = false;
wattage = Lamp.DEFAULT_WATTAGE;
name = "Lamp";
}
public Lamp(int wattage)
{
/*
Set this lamp's initial state
as being in the off position.
Set this lamp's wattage to the given wattage.
Do not let any lamp have a wattage higher than
Lamp.MAXIMUM_WATTAGE.
Give this lamp a generic name.
*/
lampIsOn = false;
if (wattage > Lamp.MAXIMUM_WATTAGE)
this.wattage = Lamp.MAXIMUM_WATTAGE;
else
this.wattage = wattage;
name = "Lamp";
}
public void turnOn()
{
/*
Turn this lamp on.
*/
lampIsOn = true;
reportState();
}
public void turnOff()
{
/*
Turn this lamp off.
*/
lampIsOn = false;
reportState();
}
public boolean isOn()
{
/*
Report whether this lamp is on or off.
*/
return lampIsOn;
}
public void reportState()
{
/*
Report this lamp's status.
*/
if (this.isOn())
{
System.out.println(this.getName() + " is on");
System.out.println("My wattage is: " + wattage);
}
else
System.out.println(this.getName() + " is off");
}
public void setName(String name)
{
/*
Name this lamp.
*/
this.name = name;
}
public String getName()
{
/*
Report this lamp's name.
*/
return name;
}
public String toString()
{
/*
Report this lamp's type and name.
*/
return "Lamp[" + this.getName() + "]";
}
}
package lights;
import lights.Lamp;
public class DimmerControlledLamp extends Lamp
{
/*
Define a type of Lamp that can brighten and darken
in stages, and compare its brightness with another lamp.
*/
//this lamp has a certain brightness, if it's on
private int brightness;
//maximum initial brightness for each dimmer-controlled lamp
public final static int INITIAL_BRIGHTNESS = 3;
public DimmerControlledLamp()
{
/*
Initialize this lamp's superclass portion,
then initialize this lamp's brightness.
*/
super();
this.setBrightness(0);
}
public DimmerControlledLamp(int wattage)
{
/*
Initialize this lamp's superclass portion,
then initialize this lamp's brightness.
*/
super(wattage);
this.setBrightness(0);
}
public void turnOn()
{
/*
Turn this lamp on gradually until it is at
DimmerControlledLamp.INITIAL_BRIGHTNESS.
This method overrides the same method in the superclass.
*/
this.setBrightness(1);
brighten(DimmerControlledLamp.INITIAL_BRIGHTNESS - 1);
}
public void turnOff()
{
/*
Turn this lamp off gradually.
This method overrides the same method in the superclass.
*/
darken(this.getBrightness());
}
public boolean isOn()
{
/*
Report whether this lamp is on.
This method overrides the same method in the superclass.
*/
return (this.getBrightness() > 0);
}
public void brighten()
{
/*
If this lamp is on, make it brighter.
*/
if (this.isOn())
this.setBrightness(this.getBrightness() + 1);
reportState();
}
public void darken()
{
/*
If this lamp is on, make it darker.
*/
if (this.isOn())
this.setBrightness(this.getBrightness() - 1);
reportState();
}
public void brighten(int brightnessIncrease)
{
/*
Make this lamp brighter in steps.
*/
for (int index = 1; index <= brightnessIncrease; index++)
brighten();
}
public void darken(int brightnessDecrease)
{
/*
Make this lamp darker in steps.
*/
for (int index = 1; index <= brightnessDecrease; index++)
darken();
}
protected void setBrightness(int brightness)
{
/*
Let another lamp, any lamps of extended classes,
or any objects in this package
set this lamp's brightness.
*/
this.brightness = brightness;
}
protected int getBrightness()
{
/*
Report this lamp's brightness to another lamp,
to any lamps of extended classes,
or to any objects in this package.
*/
return brightness;
}
public boolean isBrighterThan(DimmerControlledLamp lamp)
{
/*
Report whether this lamp is brighter than
the given lamp.
*/
return (this.getBrightness() > lamp.getBrightness());
}
public void reportState()
{
/*
Report this lamp's status.
This method overrides the same method in the superclass.
*/
if (this.isOn())
{
System.out.println(this.getName() + " is on");
System.out.println("My brightness is: " +
this.getBrightness());
}
else
System.out.println(this.getName() + " is off");
}
public String toString()
{
/*
Report this lamp's type and name.
This method overrides the same method in the superclass.
*/
return "DimmerControlledLamp[" + this.getName() + "]";
}
}
import lights.Luminous;
import lights.Lamp;
import lights.DimmerControlledLamp;
public class FiddleWithLights
{
/*
Create some lights and fiddle with them.
*/
public final static int NUMBER_OF_LAMPS = 2;
public final static int NUMBER_OF_LIGHTS = 2;
public static void main(String[] parameters)
{
//create an array of lamps reference variables
Lamp[] lampArray;
lampArray = new Lamp[FiddleWithLights.NUMBER_OF_LAMPS];
//create a 100-watt lamp
lampArray[0] = new Lamp(100);
lampArray[0].setName("Lamp number 0");
//for every other Lamp reference variable in the array,
//create a new DimmerControlledLamp and name it
for (int index = 1; index < lampArray.length; index++)
{
lampArray[index] = new DimmerControlledLamp();
lampArray[index].setName("Lamp number " + index);
}
//switch each lamp on and off
for (int index = 0; index < lampArray.length; index++)
{
System.out.println(lampArray[index]);
lampArray[index].turnOn();
lampArray[index].turnOff();
System.out.println();
}
//create an array of Luminous reference variables
Luminous[] lightsArray;
lightsArray =
new Luminous[FiddleWithLights.NUMBER_OF_LIGHTS];
//create some Luminous objects
lightsArray[0] = new Lamp();
lightsArray[1] = new DimmerControlledLamp();
//switch each light on and off
for (int index = 0; index < lightsArray.length; index++)
{
System.out.println(lightsArray[index]);
lightsArray[index].turnOn();
lightsArray[index].turnOff();
System.out.println();
}
}
}