On
completion of this section you will know:
Java is an
object oriented language, yet we have progressed this far without ever
mentioning the word object. We have managed to do this by exploiting the
fact that the method main can run
without creating an object. Thus we were able to do very limited programming
that still enabled us to become familiar with the essential ideas and
structures that underlie all programming such as:
Variables
Operators
and operands
Decision
constructs
Methods
Now that we
have got those under out belt we shall move on to examine the first OO topic
which is objects themselves and their underlying classes. In order to do so we shall revert to the
payroll application we had examined before, but this time we shall put all of
the processing into a class.
Listing
6.1 below shows our payroll application structured as a class. The processing is simplified somewhat. There
is no superannuation calculation, for example, and the calculation of the
tax has reverted to a flat 25% tax rate. The
reason for this is that at this point we assume that you are familiar with
the decision constructs, and since removing them simplifies the processing
of the class, we can concentrate better on examining the features of object
oriented programming.
Listing 6.1
|
1 |
public class Pay |
|
2 |
{ |
|
3 |
private
double hours, rate, gross, tax, nett; |
|
4 |
public void
setHours(double h) |
|
5 |
{ |
|
6 |
hours = h; |
|
7 |
} |
|
8 |
public void
setRate(double r) |
|
9 |
{ |
|
10 |
rate = r; |
|
11 |
} |
|
12 |
public
double getHours() |
|
13 |
{ |
|
14 |
return
hours; |
|
15 |
} |
|
16 |
public
double getRate() |
|
17 |
{ |
|
18 |
return
rate; |
|
19 |
} |
|
20 |
public
double getGross() |
|
21 |
{ |
|
22 |
return
gross; |
|
23 |
} |
|
24 |
public
double getTax() |
|
25 |
{ |
|
26 |
return
tax; |
|
27 |
} |
|
28 |
public
double getNett() |
|
29 |
{ |
|
30 |
return
nett; |
|
31 |
} |
|
32 |
public void
calculateGross() |
|
33 |
{ |
|
34 |
gross =
hours * rate; |
|
35 |
} |
|
36 |
public void
calculateTax() |
|
37 |
{ |
|
38 |
tax =
gross * .25; |
|
39 |
} |
|
40 |
public void
calculateNett() |
|
41 |
{ |
|
42 |
nett =
gross - tax; |
|
43 |
} |
|
44 |
} |
The first
thing to notice about Listing 6.1 is that there is no method main to be seen anywhere. Dont
worry! We shall later see how to get
around that problem. The other thing to notice is that there is
quite a large number of very small methods.
One more
thing to notice is that the variables are not declared inside a method, but in
the body of the class itself, that is at line 3. They are also declared as
being private. This means that they are visible in all of
the class but are invisible to any other class. Also notice that the methods
have all been declared as public. This means that they are visible to any other
class that wishes to interact with the class Pay.
The reason
that the variables have been declared private
is that since they cannot be seen outside of the class then they cannot be
accidentally corrupted by external code.
This is one of the features of object oriented programming that we shall
return to later. It is called encapsulation.
Of course
it is pointless having a class where all of the variables are private and
nothing outside of the class can do anything to them. It would be similar to having K1,000,000 inside a burglar-proof safe that nobody knows the
combination of. We must therefore allow some limited access to those variables. In our case this is done by making all of the
methods of the class public. This gives the outside world limited access
to those variables since that world can call the methods of the class, which in
turn can process the internal variables.
In the previous
version of the payroll application we processed the data by putting values into
the variables hours and rate, then calculating the values of gross, tax and nett and finally printing the results. We can in fact do the same thing with our
class Pay. In order to put a value
into the variable hours we simply
call the method setHours, which
spans the lines 4 7. This method takes
an argument h and in the body of the same method the value of the argument is
copied into the variable hours. Thus the outside world can put value into the
private variable hours through the setHours method.
The method setRate, which spans lines 8 11, can
update the variable rate in exactly
the same manner.
Again
notice that there is no method that allows an outsider to set the values for gross, tax or nett since those
variables should be updated from inside the class itself.
In order to
calculate the value of the gross the outsider can simply call the method calculateGross, which spans lines 32
35. In the body of this method the gross is calculated by multiplying the hours
by the rate. In a similar manner the
values of the tax and nett can be calculated.
Once the
values of the gross tax and nett are calculated the results need to be able to
be read. For this we have a range of get
methods that span lines 12 31. In our case we notice that all of them are
methods that return a double value. The
method getHours, which spans the
lines 12 15, simply uses the return
keyword to return the value of hours
to the outside world. The other get
methods do exactly the same.
Now let us
look at how we may use the class Pay
for processing an actual payroll. In order to do this we have to create another
class as shown in Listing 6.2 below.
Listing 6.2
|
1 |
public class Untitled1 |
|
2 |
{ |
|
3 |
public
static void main(String[] args) |
|
4 |
{ |
|
5 |
Pay p; |
|
6 |
p = new
Pay(); |
|
7 |
p.setHours(40); |
|
8 |
p.setRate(20); |
|
9 |
p.calculateGross(); |
|
10 |
p.calculateTax(); |
|
11 |
p.calculateNett(); |
|
12 |
System.out.println("Hours " + p.getHours()); |
|
13 |
System.out.println("Rate " + p.getRate()); |
|
14 |
System.out.println("Gross " + p.getGross()); |
|
15 |
System.out.println("Tax " + p.getTax()); |
|
16 |
System.out.println("Nett " + p.getNett()); |
|
17 |
} |
|
18 |
} |
At line 5
we declare a pointer that will eventually point to an object of the class Pay.
A pointer is simply a space in the computers memory that will hold the
address of a different memory location where an object of Pay is stored. At line 6 new Pay()
creates an object of Pay somewhere
else in memory and stores the starting address of that object in the pointer p.
What
exactly do we mean by creating an object and what is the difference between it
and the class Pay
that we have been examining up to now?
Essentially a class is a blueprint or template that is used to define
the variables to be used for an application and the methods that will process
those variables. Creating an object of
this class means finding a block of free space in memory where we can store the
variables and the code that processes them as one unit. Once this space has
been found and divided up between the variable spaces and the code that will
process them, the starting address of that block is stored in a pointer so that
any code that requires to use the newly created object
will be able to access it by referring to the pointer.
With that
out of the way let us look at the rest of the processing. At line 7 the setHours method of the object p is called and 40 is
passed to its argument. As we have seen
when examining that method earlier the value 40 will be stored will be stored
in the variable hours. The value for
the hourly rate is set in exactly the same manner. In lines 9 11 the
calculating methods are called for processing the values of the gross tax and
nett, while in lines 12 16 the results of the calculation are displayed by
calling the appropriate get methods.
In the
second sentence of the above paragraph I said At line 7 the setHours method
of the object p is called. This
is not strictly correct. The pointer p
is not the object. The object resides in a reserved block somewhere else in
memory and the pointer simply holds the address of the first register of that
block. Thus it would be more correct to
say At line 7 the setHours method of the object whose memory address is stored in the
pointer p is called. Although
correct, this very cumbersome to write or say or even to read frequently and
thus we shall use the shorter, although slightly incorrect expression which
refers to p as an object.
When discussing
Listing 6.1 we stated that the variables in the class were declared as private
so that they could not be corrupted by code that was external to the class. This is certainly true. None of them can be
corrupted directly by any external code. On
the other hand, as Listing 6.1 currently stands, the variables can certainly
be corrupted indirectly. Some examples
of external corruption are as follows:
Someone can
change line 7 of Listing 6.2 to p.setHours(9000)
and be on the plane to
At the
opposite end of the scale the same line can be changed to p.setHours(-9), which, although complete nonsense, will still be accepted by
the programme.
The
calculation of the gross, tax and nett can be performed in any order even
though they should be performed in the strict order of gross, tax and nett.
The same
calculations could occur even when no values haven been given to hours and rate.
The values
of all of the variables can be read through the get methods, even if no
calculation has ever occurred.
In order to
remedy the above weaknesses we have to make the following adjustments to our
class:
The methods
setHours and setRate must validate the incoming data to ensure that it is
within reasonable limits. We can set the
range 5 60 as an acceptable range for hours
and 8 50 as an acceptable range for rate. The same methods will set flag variables to
indicate whether correct data has been received or not
The methods
calculateGross, calculateTax and calculateNett are to be made private so that they cannot be called
directly, while a new public method
calculate will be used to call
them in the correct order.
The method calculate will first check if correct
values have been received for hours and rate before performing the calculation
of the payroll. At the end a flag will
be set indicating whether the calculation was successful or not.
The get
methods will first check if a successful calculation has occurred before
returning the values of the appropriate variables.
The code
below in Listing 6.3 is our original class which has been modified according to
the suggestions above.
Listing 6.3
|
1 |
public class Pay |
|
2 |
{ |
|
3 |
private
double hours, rate, gross, tax, nett; |
|
4 |
private
boolean validHours, validRate, calculateOK; |
|
5 |
public void
setHours(double h) |
|
6 |
{ |
|
7 |
if((h>=5) && (h<=60)) |
|
8 |
{ |
|
9 |
hours =
h; |
|
10 |
validHours = true; |
|
11 |
} |
|
12 |
else |
|
13 |
validHours = false; |
|
14 |
} |
|
15 |
public void
setRate(double r) |
|
16 |
{ |
|
17 |
if((r>=8) && (r<=50)) |
|
18 |
{ |
|
19 |
rate =
r; |
|
20 |
validRate = true; |
|
21 |
} |
|
22 |
else |
|
23 |
validRate = false; |
|
24 |
} |
|
25 |
public
double getHours() |
|
26 |
{ |
|
27 |
if(calculateOK) |
|
28 |
return
hours; |
|
29 |
else |
|
30 |
return
-1; |
|
31 |
} |
|
32 |
public
double getRate() |
|
33 |
{ |
|
34 |
if(calculateOK) |
|
35 |
return
rate; |
|
36 |
else |
|
37 |
return
-1; |
|
38 |
} |
|
39 |
public
double getGross() |
|
40 |
{ |
|
41 |
if(calculateOK) |
|
42 |
return gross; |
|
43 |
else |
|
44 |
return
-1; |
|
45 |
} |
|
46 |
public
double getTax() |
|
47 |
{ |
|
48 |
if(calculateOK) |
|
49 |
return
tax; |
|
50 |
else |
|
51 |
return
-1; |
|
52 |
} |
|
53 |
public
double getNett() |
|
54 |
{ |
|
55 |
if(calculateOK) |
|
56 |
return
nett; |
|
57 |
else |
|
58 |
return
-1; |
|
59 |
} |
|
60 |
private void
calculateGross() |
|
61 |
{ |
|
62 |
gross =
hours * rate; |
|
63 |
} |
|
64 |
private void
calculateTax() |
|
65 |
{ |
|
66 |
tax =
gross * .25; |
|
67 |
} |
|
68 |
private void
calculateNett() |
|
69 |
{ |
|
70 |
nett =
gross - tax; |
|
71 |
} |
|
72 |
public void
calculate() |
|
73 |
{ |
|
74 |
if(validHours && validRate) |
|
75 |
{ |
|
76 |
calculateGross(); |
|
77 |
calculateTax(); |
|
78 |
calculateNett(); |
|
79 |
calculateOK = true; |
|
80 |
} |
|
81 |
else |
|
82 |
calculateOK = false; |
|
83 |
} |
|
84 |
} |
The first
change we notice here is that at line 4, three new boolean variables validHours, validRate and calculateOk have been added. A boolean variable
is one that can have only one of two values true
or false. The three above will be
used by the methods setHours, setRate and calculate to set the appropriate flags as we shall see shortly.
The method setHours has undergone considerable
changes and now extends from line 5 to line 15.
The entire method now consists of an if..else construct. At line 7 the value of
the argument h is checked fro being
in the range 5 60 inclusive. If it is,
then, at line 9 that value is copied into the variable hours and validHours is
set to true. If h is outside of the range 5 60 then validHours is set to false.
Identical
alterations have been made to the method setRate.
Here we
begin to notice the power of encapsulation. Data from outside can only enter
the class through setHours and setRate. Those two methods now have complete control
of what data is allowed in. Any data not
in the allowed range is rejected.
Since setHours and setRate set the values of validHours
and validRate when we get to calculate, which spans lines 72 83,
we see that the first thing this method does is to test those two variable for
being true. This occurs at line 74. If both are true then the three methods for
calculating the payroll are called in the correct sequence at lines 76, 77 and
78 and at line 79 calculateOK is set to true. If both of the boolean variables are not true then at line 82 calculateOK is set to false.
What we
notice here regarding encapsulation is that if an attempt is made to perform
the payroll calculation before correct data has been entered for hours and rate the calculation is actually skipped and the method simply sets
calculateOK to false. Thus, when we try to read any of the
variables via one of the get methods, all those methods will have to do is
check the value of calculateOK. If it is true then they
will return the value of the appropriate variable, otherwise they will return
-1 to indicate an error situation. If we examine getGross which spans lines 39 45 we notice that this is how the
method behaves.
The reason
we can use -1 as a value to indicate an error situation is that since we are
dealing with a payroll situation the lowest value we can have for any of our
variables is 0. Thus, if we wish to indicate an error situation, we can use any
negative number we wish. The number -1 is as good as any other one.
Below at
Listing 6.4 we have the modified version of our main method. The only difference between it and its predecessor at
Listing 6.2 is that the calls to calculateGross,
calculateTax and calculateNett
have been replaced by a single call to calculate.
Listing 6.4
|
1 |
public class Untitled1 |
|
2 |
{ |
|
3 |
public
static void main(String[] args) |
|
4 |
{ |
|
5 |
Pay p; |
|
6 |
p = new
Pay(); |
|
7 |
p.setHours(40); |
|
8 |
p.setRate(20); |
|
9 |
p.calculate(); |
|
10 |
System.out.println("Hours " + p.getHours()); |
|
11 |
System.out.println("Rate " + p.getRate()); |
|
12 |
System.out.println("Gross " + p.getGross()); |
|
13 |
System.out.println("Tax " + p.getTax()); |
|
14 |
System.out.println("Nett " + p.getNett()); |
|
15 |
} |
|
16 |
} |
We now come
to a point where we can explain a little more about the keywords that precede
our main method in this case
the public keyword. If a method is to be seen outside of its class
it has to be declared as public. The method main is that startup method of any application, and as it has to be
called by the operator from outside of the class that contains it, it has
to be declared as public.
A class
is a template for an object. At runtime an object of a class is created using
the keyword new. The object is
simply a block of reserved memory which is set up to contain the variables
and the code specified in the class.
When an
object is created its memory address is returned and is usually stored in a
pointer. This pointer is subsequently
used to access any of the methods of the class.
One of the
advantages of using an object is that we can encapsulate our data inside it.
Some of the guidelines of encapsulation are:
Declare all
variables as private
Allow
external data into the object via set methods.
Incoming
data should be validated by the set methods and rejected if not correct
only
essential input data should be allowed in
any
methods required only for internal processing should be declared as private
any
variables used only for internal processing should not have either get or set
methods
set
methods should return only valid data
Copy listings
6.3 and 6.4 into your computer, then compile and run them. Next experiment
with listing 6.4 by passing values that are outside of the range for hours
and rate and check that -1s are returned.
Finally try
to make calls to the private methods or variables to check how the system
handles these error situations.
1.
what is the difference between a class and an
object?
2.
what is a pointer?
3.
describe in detail the workings of the keyword new.
4.
what is the meaning of encapsulation?
5.
describe
the structure and the use of set methods in a class
6.
describe
the structure and the use of get methods in a class
7.
list
some techniques for enhancing the encapsulation of data
Modify exercise
5.2 as follows:
Use both
listings 6.3 and 6.4 as models for this exercise.