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

 


Objectives:

To gain experience with:

 

1.         Abstract Classes.

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:

  

 

2.         Interfaces

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);

      }

}

 

 

3.         Similarities & Differences between Classes, Abstract classes and Interfaces

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 ";" if declared abstract.

Must be ";"
Do not declare abstract because all methods are abstract implicitly.

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.

 

 

4.         Assignment:

  1. Modify the Shape, Line, Rectangle, Ellipse and Square problem of lab01 by declaring the Shape as an abstract class and defining the draw method of Shape as abstract.  If necessary, modify the other classes as appropriate.

 

  1. Pull-out the draw method from the Shape class and put it into an interface called Drawable.  Modify the other classes as appropriate and renamed them as Shape1, Line1, etc.