INFORMATION & COMPUTER SCIENCE DEPARTMENT, KFUPM
ICS201, SECTIONS 02 (002
Semester)
INTRODUCTION TO COMPUTER SCIENCE
LAB #03 Practice with Abstract classes and Interfaces
Instructor: Bashir M. Ghandi
To gain experience with:
When
an existing class is extended, the user (i.e. the subclass programmer) has the
option of whether or not to override the methods of the super class. Sometimes we may wish to force the user to
override a method. This is usually the
case if there is no good default method in the super class or when only the
subclass programmer can know how to implement the method. To achieve this, we declare the methods we
wish to force the user to override as abstract. A class that has one or more abstract
methods is called an abstract class and must be declared as such.
Consider
the classes, Worker, HourlyWorker and SalariedWorker
introduced in lab01, where the salary of the SalariedWorker is computed by
simply multiplying the pay rate with the normal working hours of 40, regardless
of the number of hours worked. And
where the HourlyWorker is paid at 1.5 times pay rate for any hour in excess of
40.
Since
the computation is different for the two different workers, it does not make
sense to define the method computePay in the super class. We may define the method as a do nothing
method and hope that the user will realize that he has to implement it. A better approach however is to force the
user to do the implementation by declaring the method as abstract. The following example shows how this may be
done.
package cs.databases; public abstract
class Worker { protected
String name; protected
int hours; protected
double payRate; static
final int NORMAL_HOURS = 40; public
Worker(String name, double payRate) { this.name
= name; this.payRate
= payRate; } public
Worker(String name, int hours, double payRate) { this(name,
payRate); this.hours
= hours; } public
String toString() { return
"Name:"+name+", Pay Rate: "+payRate; } abstract double computePay(); } |
package cs.databases; public class HourlyWorker extends Worker { public HourlyWorker(String
name, int hours, double payRate) { super(name,
hours, payRate); } public
double computePay() { if
(hours <= NORMAL_HOURS) return
hours*payRate; else return NORMAL_HOURS*payRate +
(hours-NORMAL_HOURS)*1.5*payRate; } public
String toString() { return
super.toString() + ", Hours: "+hours+", Salary:
"+computePay(); } } |
package cs.databases; public class SalariedWorker extends Worker { public
SalariedWorker(String name, double payRate) { super(name,
payRate); } public double computePay() { return
NORMAL_HOURS*payRate; } public
String toString() { return
super.toString() + ", Salary: "+computePay(); } } |
package cs.labs.lab03; import cs.databases.*; public class TestWorkers { public
static void main(String[] args) { Worker
worker1 = new SalariedWorker("Usman", 20); Worker
worker2 = new HourlyWorker("Amjad", 45, 10); System.out.println(worker1); System.out.println(worker2); } } |
Notice
that:
Some
OO languages such as C++ allow a sub-class to inherit from more than one super
class (multiple inheritance). While
this has some advantages, it makes such languages complex. To avoid such complexities, Java does not
allow for multiple inheritance.
However, a lot of the advantages of multiple inheritance can be achieved
using Interfaces.
An
interface is similar to a class but with the following restrictions:
The
following example modifies the workers example above by extracting the
computePay method and the constant NORMAL_HOURS into an interface called SalaryCalculator:
package cs.databases; interface
SalaryCalculator { int
NORMAL_HOURS = 40; double
computePay(); } |
package cs.databases; public class Worker1 { protected
String name; protected
int hours; protected
double payRate; public
Worker1(String name, double payRate) { this.name
= name; this.payRate
= payRate; } public
Worker1(String name, int hours, double payRate) { this(name,
payRate); this.hours
= hours; } public
String toString() { return
"Name:"+name+", Pay Rate: "+payRate; } } |
package cs.databases; public class HourlyWorker1 extends Worker1 implements SalaryCalculator { public
HourlyWorker1(String name, int hours, double payRate) { super(name,
hours, payRate); } public
double computePay() { if
(hours <= NORMAL_HOURS) return
hours*payRate; else return NORMAL_HOURS*payRate + (hours-NORMAL_HOURS)*1.5*payRate; } public
String toString() { return
super.toString() + ", Hours: "+hours+", Salary:
"+computePay(); } } |
package cs.databases; public class SalariedWorker1 extends Worker1 implements SalaryCalculator { public
SalariedWorker1(String name, double payRate) { super(name,
payRate); } public
double computePay() { return
NORMAL_HOURS*payRate; } public
String toString() { return
super.toString() + ", Salary: "+computePay(); } } |
package cs.labs.lab03; import cs.databases.*; public class TestWorkers { public
static void main(String[] args) { Worker1
worker1 = new SalariedWorker1("Usman", 20); Worker1
worker2 = new HourlyWorker1("Amjad", 45, 10); System.out.println(worker1); System.out.println(worker2); } } |
The following table summarizes the differences and similarities between classes, abstract classes and interfaces:
|
class |
abstract
class |
interface |
Can it be instantiated? |
Yes |
No |
No |
Can it be extended? |
Yes |
Yes |
Not really, but can be
implemented instead |
Method body? |
Must be { … } |
May be { … } |
Must be ";" |
Instance methods? |
OK |
OK |
No |
Static methods? |
OK |
OK |
No |
Abstract methods? |
No |
OK |
All methods in interfaces
are abstract implicitly. |
Instance fields? |
OK |
OK |
No. |
Static fields? |
OK |
OK |
Yes, but do not declare
static because all fields are public, static, and final implicitly. |