411 REFERENCE · Wikibooks
The Way of the Java/Object-oriented programming
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Object-oriented programming
Programming languages and styles
programming language
language!programming
programming style
object-oriented programming
functional programming
procedural programming
programming!object-oriented
programming!functional
programming!procedural
There are many programming languages in the world, and almost as many
programming styles (sometimes called paradigms). Three styles that
have appeared in this book are procedural, functional, and
object-oriented. Although Java is usually thought of as an
object-oriented language, it is possible to write Java programs in any
style. The style I have demonstrated in this book is pretty much
procedural. Existing Java programs and the built-in Java packages are
written in a mixture of all three styles, but they tend to be more
object-oriented than the programs in this book.
It's not easy to define what object-oriented programming is,
but here are some of its characteristics:
itemize
Object definitions (classes) usually correspond to
relevant real-world objects. For example, in Chapter deck,
the creation of the Deck class was a step toward object-oriented
programming.
The majority of methods are object methods (the kind you
invoke on an object) rather than class methods (the kind you just
invoke). So far all the methods we have written have been class
methods. In this chapter we will write some object methods.
The language feature most associated with object-oriented
programming is inheritance. I will cover inheritance later in
this chapter.
itemize
inheritance
Recently object-oriented programming has become quite popular, and
there are people who claim that it is superior to other styles in
various ways. I hope that by exposing you to a variety of styles I
have given you the tools you need to understand and evaluate these
claims.
Object and class methods
object method
method!object
class method
method!class
static
There are two types of methods in Java, called class methods and
object methods. So far, every method we have written has been a
class method. Class methods are identified by the keyword
static in the first line. Any method that does not have the keyword
static is an object method.
Although we have not written any object methods, we have invoked some.
Whenever you invoke a method on an object, it's an object method.
For example, drawOval is an object method we invoked on g,
which is a Graphics object. Also, the methods we invoked on
Strings in Chapter strings were object methods.
Graphics
class!Graphics
Anything that can be written as a class method can also be written as an
object method, and vice versa. Sometimes it is just more natural to
use one or the other. For reasons that will be clear soon, object
methods are often shorter than the corresponding class methods.
The current object
current object
object!current
this
When you invoke a method on an object, that object becomes the
current object. Inside the method, you can refer to the instance
variables of the current object by name, without having to specify the
name of the object.
constructor
Also, you can refer to the current object using the keyword
this. We have already seen this used in constructors. In
fact, you can think of constructors as being a special kind of object
method.
Complex numbers
complex number
Complex
class!Complex
arithmetic!complex
As a running example for the rest of this chapter we will consider a
class definition for complex numbers. Complex numbers are useful for
many branches of mathematics and engineering, and many computations
are performed using complex arithmetic. A complex number is the sum
of a real part and an imaginary part, and is usually written in the
form , where is the real part, is the imaginary part,
and represents the square root of -1.
Thus, .
The following is a class definition for a new object type called
Complex:
verbatim
class Complex
// instance variables
double real, imag;
// constructor
public Complex ()
this.real = 0.0; this.imag = 0.0;
// constructor
public Complex (double real, double imag)
this.real = real; this.imag = imag;
verbatim
There should be nothing surprising here. The instance variables
are two doubles that contain the real and imaginary parts.
The two constructors are the usual kind: one takes no parameters
and assigns default values to the instance variables, the other
takes parameters that are identical to the instance variables.
As we have seen before, the keyword this is used to refer
to the object being initialized.
instance variable
variable!instance
constructor
In main, or anywhere else we want to create Complex
objects, we have the option of creating the object and then
setting the instance variables, or doing both at the same time:
verbatim
Complex x = new Complex ();
x.real = 1.0;
x.imag = 2.0;
Complex y = new Complex (3.0, 4.0);
verbatim
A function on Complex numbers
operator!Complex
method!function
pure function
Let's look at some of the operations we might want to perform
on complex numbers. The absolute value of a complex number is
defined to be . The abs method is
a pure function that computes the absolute value. Written as
a class method, it looks like this:
verbatim
// class method
public static double abs (Complex c)
return Math.sqrt (c.real * c.real + c.imag * c.imag);
verbatim
This version of abs calculates the absolute value of c,
the Complex object it receives as a parameter. The next version
of abs is an object method; it calculates the absolute value of
the current object (the object the method was invoked on). Thus,
it does not receive any parameters:
verbatim
// object method
public double abs ()
return Math.sqrt (real*real + imag*imag);
verbatim
I removed the keyword static to indicate that this is an object
method. Also, I eliminated the unnecessary parameter. Inside the
method, I can refer to the instance variables real and
imag by name without having to specify an object. Java knows
implicitly that I am referring to the instance variables of the
current object. If I wanted to make it explicit, I could have used
the keyword this:
verbatim
// object method
public double abs ()
return Math.sqrt (this.real * this.real + this.imag * this.imag);
verbatim
But that would be longer and not really any clearer. To invoke
this method, we invoke it on an object, for example
verbatim
Complex y = new Complex (3.0, 4.0);
double result = y.abs();
verbatim
Another function on Complex numbers
Another operation we might want to perform on complex numbers
is addition. You can add complex numbers by adding the real
parts and adding the imaginary parts. Written as a class method,
that looks like:
verbatim
public static Complex add (Complex a, Complex b)
return new Complex (a.real + b.real, a.imag + b.imag);
verbatim
To invoke this method, we would pass both operands as arguments:
verbatim
Complex sum = add (x, y);
verbatim
Written as an object method, it would take only one argument,
which it would add to the current object:
verbatim
public Complex add (Complex b)
return new Complex (real + b.real, imag + b.imag);
verbatim
Again, we can refer to the instance variables of the current
object implicitly, but to refer to the instance variables of
b we have to name b explicitly using dot notation.
To invoke this method, you invoke it on one of the operands
and pass the other as an argument.
dot notation
verbatim
Complex sum = x.add (y);
verbatim
From these examples you can see that the current object (this)
can take the place of one of the parameters. For this reason,
the current object is sometimes called an implicit parameter.
A modifier
modifier
method!modifier
As yet another example, we'll look at conjugate, which is
a modifier method that transforms a Complex number into
its complex conjugate. The complex conjugate of is
.
As a class method, this looks like:
verbatim
public static void conjugate (Complex c)
c.imag = -c.imag;
verbatim
As an object method, it looks like
verbatim
public void conjugate ()
imag = -imag;
verbatim
By now you should be getting the sense that converting a method
from one kind to another is a mechanical process. With a little
practice, you will be able to do it without giving it much
thought, which is good because you should not be constrained to
writing one kind of method or the other. You should be equally
familiar with both so that you can choose whichever one seems
most appropriate for the operation you are writing.
For example, I think that add should be written as a class
method because it is a symmetric operation of two operands, and
it makes sense for both operands to appear as parameters. It just
seems odd to invoke the method on one of the operands and pass
the other as an argument.
On the other hand, simple operations that apply to a single object
can be written most concisely as object methods (even if they
take some additional arguments).
The toString method
toString
method!toString
There are two object methods that are common to many object
types: toString and equals. toString converts
the object to some reasonable string representation that can
be printed. equals is used to compare objects.
When you print an object using print or println,
Java checks to see whether you have provided an object method
named toString, and if so it invokes it. If not, it
invokes a default version of toString that produces
the output described in Section printobject.
Here is what toString might look like for the Complex
class:
verbatim
public String toString ()
return real + " + " + imag + "i";
verbatim
The return type for toString is String, naturally,
and it takes no parameters. You can invoke toString in
the usual way:
verbatim
Complex x = new Complex (1.0, 2.0);
String s = x.toString ();
verbatim
or you can invoke it indirectly through print:
verbatim
System.out.println (x);
verbatim
Whenever you pass an object to print or println, Java
invokes the toString method on that object and prints the result.
In this case, the output is 1.0 + 2.0i.
This version of toString does not look good if the imaginary
part is negative. As an exercise, fix it.
The equals method
equals
method!equals
When you use the == operator to compare two objects,
what you are really asking is, Are these two things the same object? That is, do both objects refer to the same location
in memory.
For many types, that is not the appropriate definition of
equality. For example, two complex numbers are equal if their
real parts are equal and their imaginary parts are equal.
type!object
When you create a new object type, you can provide your own
definition of equality by providing an object method called
equals. For the Complex class, this looks like:
verbatim
public boolean equals (Complex b)
return (real == b.real && imag == b.imag);
verbatim
By convention, equals is always an object method. The return
type has to be boolean.
The documentation of equals in the Object class
provides some guidelines you should keep in mind when you
make up your own definition of equality:
quote
The equals method implements an equivalence relation:
equality
identity
itemize
It is reflexive: for any reference value x,
x.equals(x) should return true.
It is symmetric: for any reference values x and y,
x.equals(y) should return true if and only if
y.equals(x) returns true.
It is transitive: for any reference values x, y, and
z, if x.equals(y) returns true and y.equals(z)
returns true, then x.equals(z) should return true.
It is consistent: for any reference values x and y,
multiple invocations of x.equals(y) consistently return true or
consistently return false.
For any reference value x, x.equals(null) should
return false.
itemize
quote
The definition of equals I provided satisfies all these
conditions except one. Which one? As an exercise, fix it.
Invoking one object method from another
method!invoking
As you might expect, it is legal and common to invoke
one object method from another. For example, to normalize a
complex number, you divide through (both parts) by the absolute
value. It may not be obvious why this is useful, but it is.
Let's write the method normalize as an object method, and
let's make it a modifier.
verbatim
public void normalize ()
double d = this.abs();
real = real/d;
imag = imag/d;
verbatim
The first line finds the absolute value of the current object
by invoking abs on the current object. In this case
I named the current object explicitly, but I could have left
it out. If you invoke one object method within another, Java
assumes that you are invoking it on the current object.
As an exercise, rewrite normalize as a pure function.
Then rewrite it as a class method.
Oddities and errors
method!object
method!class
overloading
If you have both object methods and class methods in the same class
definition, it is easy to get confused. A common way to organize a
class definition is to put all the constructors at the beginning,
followed by all the object methods and then all the class methods.
You can have an object method and a class method with the same
name, as long as they do not have the same number and types of
parameters. As with other kinds of overloading, Java decides
which version to invoke by looking at the arguments you provide.
static
Now that we know what the keyword static means, you
have probably figured out that main is a class method,
which means that there is no current object when it is invoked.
current object
this
instance variable
variable!instance
Since there is no current object in a class method, it is an
error to use the keyword this. If you try, you might get
an error message like: Undefined variable: this. Also, you
cannot refer to instance variables without using dot notation
and providing an object name. If you try, you might get
Can't make a static reference to nonstatic variable...
This is not one of the better error messages, since it uses
some non-standard language. For example, by nonstatic variable it means instance variable. But once you know
what it means, you know what it means.
Inheritance
inheritance
The language feature that is most often associated with
object-oriented programming is inheritance. Inheritance is the
ability to define a new class that is a modified version of a
previously-defined class (including built-in classes).
The primary advantage of this feature is that you can add new methods
or instance variables to an existing class without modifying the
existing class. This is particularly useful for built-in classes,
since you can't modify them even if you want to.
The reason inheritance is called inheritance is that the
new class inherits all the instance variables and methods
of the existing class. Extending this metaphor, the existing
class is sometimes called the parent class.
Drawable rectangles
Rectangle
class!Rectangle
drawable
An an example of inheritance, we are going to take the existing
Rectangle class and make it drawable. That is, we are going to
create a new class called DrawableRectangle that will have all
the instance variables and methods of a Rectangle, plus an
additional method called draw that will take a Graphics
object as a parameter and draw the rectangle.
The class definition looks like this:
verbatim
import java.awt.*;
class DrawableRectangle extends Rectangle
public void draw (Graphics g)
g.drawRect (x, y, width, height);
verbatim
Yes, that's really all there is in the whole class definition. The
first line imports the java.awt package, which is where
Rectangle and Graphics are defined.
AWT
import
statement!import
The next line indicates that DrawableRectangle inherits from
Rectangle. The keyword extends is used to identify the
parent class.
The rest is the definition of the draw method, which refers to
the instance variables x, y, width and height.
It might seem odd to refer to instance variables that don't appear in
this class definition, but remember that they are inherited from the
parent class.
To create and draw a DrawableRectangle, you could use
the following:
verbatim
public static void draw
(Graphics g, int x, int y, int width, int height)
DrawableRectangle dr = new DrawableRectangle ();
dr.x = 10; dr.y = 10;
dr.width = 200; dr.height = 200;
dr.draw (g);
verbatim
The parameters of draw are a Graphics object and
the bounding box of the drawing area (not the coordinates of the
rectangle).
It might seem odd to use the new command for a class
that has no constructors. DrawableRectangle
inherits the default constructor of its parent class, so there
is no problem there.
constructor
We can set the instance variables of dr and invoke methods
on it in the usual way. When we invoke draw, Java invokes
the method we defined in DrawableRectangle. If we invoked
grow or some other Rectangle method on dr, Java
would know to use the method defined in the parent class.
The class hierarchy
class hierarchy
Object
parent class
class!parent
In Java, all classes extend some other class. The most basic class is
called Object. It contains no instance variables, but it does
provide the methods equals and toString, among others.
Many classes extend Object, including almost all of the classes
we have written and many of the built-in classes, like
Rectangle. Any class that does not explicitly name a parent inherits
from Object by default.
Some inheritance chains are longer, though. For example, Slate
extends Frame (see Appendix slate), which
extends Window, which extends Container, which extends
Component, which extends Object. No matter how long the
chain, Object is the ultimate parent of all classes.
All the classes in Java can be organized into a family tree that
is called the class hierarchy. Object usually appears at the
top, with all the child classes below. If you look at the
documentation of Frame, for example, you will see the part of
the hierarchy that makes up Frame's pedigree.
Object-oriented design
object-oriented design
Inheritance is a powerful feature. Some programs that would be
complicated without inheritance can be written concisely and simply
with it. Also, inheritance can facilitate code reuse, since you can
customize the behavior of build-in classes without having to modify
them.
On the other hand, inheritance can make programs difficult to read,
since it is sometimes not clear, when a method is invoked, where to
find the definition. For example, one of the methods you can invoke
on a Slate is getBounds. Can you find the documentation
for getBounds? It turns out that getBounds is defined in
the parent of the parent of the parent of the parent of Slate.
Also, many of the things that can be done using inheritance can
be done almost as elegantly (or more so) without it.
Glossary
description
[object method:] A method that is invoked on an object,
and that operates on that object, which is referred to by
the keyword this in Java or the current object in
English. Object methods do not have the keyword static.
[class method:] A method with the keyword static.
Class methods are not invoked on objects and they do not have
a current object.
[current object:] The object on which an object method
is invoked. Inside the method,
the current object is referred to by this.
[this:] The keyword that refers to the current object.
[implicit:] Anything that is left unsaid or implied. Within
an object method, you can refer to the instance variables
implicitly (without naming the object).
[explicit:] Anything that is spelled out completely. Within
a class method, all references to the instance variables have to
be explicit.
object method
class method
current object
this
implicit
explicit