MODULE 5q - An interface


A SECOND (TRULY EVIL!) VARIATION OF ShapeA - INTRODUCTION

The versions of the ShapeA program introduced so far have involved a
parent class Shape and two child classes Square and Circle:

                            Shape
                            /   \
                           /     \
                          /       \
                       Square   Circle

In the most recent version of the ShapeA program, a polymorphic array
was set up consisting of a mixture elements, some of type Square and
some of type Circle.  The elements were sorted into ascending order of
area.

To achieve this goal, the class definition of Square and the class
definition of Circle each incorporated the following compare() method:

   public boolean compare(Shape that)
    { return this.area() > that.area();
    }

It was noted that the duplication of the compare() method was an
example of poor style.  An altogether better approach will be
described later in the course but, for the purposes of illustration,
the next few variations are going to be of poorer style still!

It would, of course, be trivial to sort a mixture of Squares and
Circles by perimeter instead of by area.  The duplicated compare()
method would simply be changed to:

   public boolean compare(Shape that)
    { return this.perimeter() > that.perimeter();
    }

This is a straightforward change but now let's get awkward!

Suppose that when two Shapes are compared, instead of comparing their
areas or their perimeters, one compares their sides (if both are
Squares) or their radii (if both are Circles) or a side and a radius
if there is one of each.  Since a side and a radius both have the
dimensions of length it is not unreasonable to compare one against the
other.

Such a wish considerably complicates the compare() methods and the two
will no longer be the same.  This explains why the fix suggested
earlier for avoiding duplication was not carried out.  A first
attempt at the compare() method in class Square might be:

   public boolean compare(Shape that)
    { return this.side > that.side;
    }

The problem lies in the argument  that  whose type is given simply as
the parent type Shape.  This version of compare() would stand a chance
if  that  happens to be a Square because  that.side  is meaningful.
It is not meaningful if  that  is of type Circle when the method would
more sensibly be:

   public boolean compare(Shape that)
    { return this.side > that.radius;
    }

In fact, neither of these methods can ever work because  that  has
type Shape and the compiler cannot find a data field  side  or a
data field  radius  in class Shape.

One way to proceed (or dig oneself into a deeper hole!) is to use the
operator  instanceof  as in:

   public boolean compare(Shape that)
    { return this.side > (that instanceof Square
                          ? ((Square)that).side
                          : ((Circle)that).radius);
    }

The value of   that instanceof Square   is true if  that  happens to
be of type Square and false otherwise.  Even if true, one still cannot
write  that.Side  but has first to cast  that  to type  Square  by
means of   (Square)that   as shown (and this has to be in brackets).
Likewise one needs the cast  (Circle)that  if  that  is a Circle. 

Note that there is no need to cast  this  because the method is being
defined in class Square so  this  is guaranteed to be of type Square.

In a similar way the compare() method in class Circle might be:

   public boolean compare(Shape that)
    { return this.radius > (that instanceof Circle
                            ? ((Circle)that).radius
                            : ((Square)that).side);
    }


A SECOND VARIATION - PROGRAM

The new version of the ShapeA program is shown below.  All changes to
the previous version of the ShapeA program are indicated by comments.

The revised compare() methods are incorporated into class Square and
class Circle.  The compare() method in class Square needs to access
the data field radius in class Circle so this data field can no longer
be private (likewise data field side needs to be accessible from class
Circle).  These changes from private to public constitute more bad
news.

The principal reason that the programming style is so bad is most
easily appreciated if you consider adding a new child of class Shape,
say a Triangle. This would require every compare() method in the
existing child classes of Shape to be modified, a sure recipe for
disaster!

Using the type of an item as a means of deciding which variant of an
operation to perform is one of the classic examples of `a bad thing in
traditional programming which object oriented programming is supposed
to allow us to avoid'.

Each object should know how to do the operation on itself and
shouldn't have to worry about what the other objects do (or even
whether they exist).

Set up this version now.


public class ShapeA
 { public static void main(String[] args)
    { Shape[] sa = {new Square("Trafalgar", 2d),
                    new Square("Leicester", 3d),
                    new Circle("Arctic", 1.5d)};
      printOut(sa);
      sort(sa);
      printOut(sa);
    }

   private static void printOut(Shape[] s)
    { for (int i=0; i<s.length; i++)
         System.out.println("sa[" + i + "]: " + s[i]);
    }

   private static void sort(Shape[] s)
    { for (int k=1; k<s.length; k++)
       { int i=k;
         while (i>0 && s[i-1].compare(s[i]))
          { Shape t = s[i-1];
            s[i-1] = s[i];
            s[i] = t;
            i--;
          }
       }
    }
 }

abstract class Shape
 { private String name;

   public Shape(String s)
    { setName(s);
    }

   public String getName()
    { return this.name;
    }

   public void setName(String s)
    { this.name = s;
    }

   public abstract double perimeter();

   public abstract double area();

   public abstract boolean compare(Shape that);
 }


class Square extends Shape
 { public double side;                                // now public

   public Square(String s, double side)
    { super(s);
      this.side = side;
    }

   public double perimeter()
    { return 4d*this.side;
    }

   public double area()
    { return this.side*this.side;
    }

   public boolean compare(Shape that)                 // new EVIL compare()
    { return this.side > (that instanceof Square
                          ? ((Square)that).side
                          : ((Circle)that).radius);
    }

   public String toString()
    { return "  Square - " + this.getName() + "\n" +
             "  Side is " + this.side + "\n" +
             "  Perimeter is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }

class Circle extends Shape
 { public double radius;                              // now public

   public Circle(String s, double radius)
    { super(s);
      this.radius = radius;
    }

   public double perimeter()
    { return 2d*Math.PI*this.radius;
    }

   public double area()
    { return Math.PI*this.radius*this.radius;
    }

   public boolean compare(Shape that)                 // new EVIL compare()
    { return this.radius > (that instanceof Circle
                            ? ((Circle)that).radius
                            : ((Square)that).side);
    }

   public String toString()
    { return "  Circle - " + this.getName() + "\n" +
             "  Radius is " + this.radius + "\n" +
             "  Circumference is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }


WHY IS THE CODE SO BAD?

The two most recent versions of the ShapeA program have prompted
four complaints:

  1.  As a name for a method  compare()  is not very satisfactory.

  2.  Duplicating code is usually bad news.

  3.  Using the type of an item as a means of deciding which variant
      of an operation to perform is very bad practice.

  4.  Requiring data fields  side  and  radius  to be declared public
      is bad form.

The latest version has attended to the second complaint and made
things worse!  As noted earlier, a better approach will be left
pending.

Meanwhile the present version will be tested and then two further
variations will be described to illustrate an alternative form of
inheritance in Java.


TRY IT OUT

Compile and run the program.  The output should be:

sa[0]:   Square - Trafalgar
  Side is 2.0
  Perimeter is 8.0
  Area is 4.0

sa[1]:   Square - Leicester
  Side is 3.0
  Perimeter is 12.0
  Area is 9.0

sa[2]:   Circle - Arctic
  Radius is 1.5
  Circumference is 9.42477796076938
  Area is 7.0685834705770345

sa[0]:   Circle - Arctic
  Radius is 1.5
  Circumference is 9.42477796076938
  Area is 7.0685834705770345

sa[1]:   Square - Trafalgar
  Side is 2.0
  Perimeter is 8.0
  Area is 4.0

sa[2]:   Square - Leicester
  Side is 3.0
  Perimeter is 12.0
  Area is 9.0

This output is much as before except that the order of the sorted
elements will be different.


MULTIPLE INHERITANCE

Consider again abstract class Shape and the abstract methods in it:

abstract class Shape
 { private String name;

   public Shape(String s)
    { setName(s);
    }

   public String getName()
    { return this.name;
    }

   public void setName(String s)
    { this.name = s;
    }

   public abstract double perimeter();

   public abstract double area();

   public abstract boolean compare(Shape that);
 }

Informally, the abstract methods are simply the headings of methods
which some program designer thinks should be included in child classes
which represent geometric figures.

Both perimeter() and area() are obvious choices since they strongly
relate to Shapes.  By contrast, compare() could apply much more
generally since many other things apart from Shapes can be compared,
for example Strings, goal averages and prices.

This leads to the idea that one might have TWO parent classes.  In
the present case, the abstract methods which strongly relate to Shapes
would be in one and the abstract method which applies less specifically
might be in the other.  Keeping the name Shape for the first parent
class and introducing the name Sortable for the other, the inheritance
diagram might be depicted thus:

                        Shape  Sortable
                         |   \ /   |
                         |    X    |
                         |   / \   |
                       Square   Circle





In outline the two parent classes might be written as:

abstract class Shape
 { .
   .

   public abstract double perimeter();

   public abstract double area();
 }

abstract class Sortable
 { .
   .

   public abstract boolean compare(Sortable s);
 }

Only the abstract methods in each class are shown and class Shape is
as it was originally; it specifies methods which are fairly narrowly
applicable to geometric figures.  The idea of class Sortable is that
given any two descendents it should be possible to compare them.  In
other words they are `Sortable'.  Notice that the argument of
compare() is now of type Sortable.

The separation achieved by having these two parent classes naturally
leads to a concept known as `multiple inheritance'.  In particular it
seems that class Square and class Circle would inherit from both Shape
and Sortable.  One might hope Java allowed syntax such as:

class Square extends Shape, Sortable
 { ...

Unfortunately, this is not permitted.  The designers of Java decided
that general-purpose multiple inheritance is full of hazards and the
disadvantages outweigh the advantages.  Nevertheless, Java permits a
limited form of multiple inheritance via interfaces...


AN interface

An interface takes the idea of an abstract class one step further.
It is almost true to say that

         an interface is a restricted form of abstract class

in that it can contain abstract methods only.  [In fact it may also
contain data fields but these are implicitly both static and final;
an interface certainly cannot contain any non-abstract methods.]

Given the restrictions, inheriting from an interface is nothing like
as big a deal as inheriting from a class (whether abstract or not) and
Java allows a child class to inherit from any number of interfaces.
Such a child class may additionally inherit from a single class which
may be abstract or non-abstract.

The abstract class Shape includes data fields and non-abstract methods
so cannot be an interface.  The abstract class Sortable doesn't need
to contain anything other than the method heading for compare() so it
can readily be converted into an interface.

An appropriate inheritance diagram to illustrate the proposed
relationships is:

                            Shape     .Sortable
                            /   \  . .
                           /   . \  .
                          /.      \.
                       Square   Circle

Dashes represent proper inheritance (as by a sub-class from a super
class) and dots represent the lesser form of inheritance (by a
sub-class from an interface).

Two principal changes will be incorporated in the next version of the
program shown below.  First, the abstract method compare() will be
removed from class Shape (thereby restoring Shape to its original
form).

Secondly, the proposed abstract class Sortable (which is the new home
of the compare() method) will be converted into an interface; this is
achieved simply by replacing  abstract class  by  interface  in the
heading line:

interface Sortable
 { public abstract boolean compare(Sortable s);
 }

The child classes Square and Circle will need to inherit from the
abstract class Shape AND from the interface Sortable.  This is
achieved by using the qualifier  implements  in the heading line
as in this example for class Square:

class Square extends Shape implements Sortable
 { ...

Note that a child class `extends' a parent class but `implements'
an interface.


A THIRD VARIATION

The complete revised version of the ShapeA program is shown below.
All changes to the previous version are indicated by comments.

Note that individual Shapes like Squares and Circles inherit from both
Shape and Sortable and either Shape or Sortable can be used as a
generic type to describe a mixture of Squares and Circles.

When setting up the polymorphic array, Sortable is decidedly the more
appropriate type to describe the elements since it is the Sortable
interface that specifies the compare() method.  This choice is also
consistent with the earlier decision to have the argument of the
compare() method of type Sortable.

In the program the type of the polymorphic array  sa  is Sortable
and the type of the formal argument of both printOut() and Sort()
is also Sortable and not Shape.  Set up this version now.


public class ShapeA
 { public static void main(String[] args)             // type Sortable now
    { Sortable[] sa = {new Square("Trafalgar", 2d),
                       new Square("Leicester", 3d),
                       new Circle("Arctic", 1.5d)};
      printOut(sa);
      sort(sa);
      printOut(sa);
    }

   private static void printOut(Sortable[] s)         // type Sortable
    { for (int i=0; i<s.length; i++)
         System.out.println("sa[" + i + "]: " + s[i]);
    }

   private static void sort(Sortable[] s)             // type Sortable
    { for (int k=1; k<s.length; k++)
       { int i=k;
         while (i>0 && s[i-1].compare(s[i]))
          { Sortable t = s[i-1];                      // type Sortable
            s[i-1] = s[i];
            s[i] = t;
            i--;
          }
       }
    }
 }

abstract class Shape
 { private String name;

   public Shape(String s)
    { setName(s);
    }

   public String getName()
    { return this.name;
    }

   public void setName(String s)
    { this.name = s;
    }

   public abstract double perimeter();

   public abstract double area();
                                                      // compare() removed
 }

interface Sortable                                    // new interface
 { public abstract boolean compare(Sortable that);    // type Sortable
 }

class Square extends Shape implements Sortable        // implements Sortable
 { public double side;

   public Square(String s, double side)
    { super(s);
      this.side = side;
    }

   public double perimeter()
    { return 4d*this.side;
    }

   public double area()
    { return this.side*this.side;
    }

   public boolean compare(Sortable that)              // type Sortable
    { return this.side > (that instanceof Square
                          ? ((Square)that).side
                          : ((Circle)that).radius);
    }

   public String toString()
    { return "  Square - " + this.getName() + "\n" +
             "  Side is " + this.side + "\n" +
             "  Perimeter is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }

class Circle extends Shape implements Sortable        // implements Sortable
 { public double radius;

   public Circle(String s, double radius)
    { super(s);
      this.radius = radius;
    }

   public double perimeter()
    { return 2d*Math.PI*this.radius;
    }

   public double area()
    { return Math.PI*this.radius*this.radius;
    }

   public boolean compare(Sortable that)              // type Sortable
    { return this.radius > (that instanceof Circle
                            ? ((Circle)that).radius
                            : ((Square)that).side);
    }

   public String toString()
    { return "  Circle - " + this.getName() + "\n" +
             "  Radius is " + this.radius + "\n" +
             "  Circumference is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }


The program in general, and the compare() methods in particular, still
reflect appalling practice.  Improvements will be provided later!


TRY IT OUT

Compile and run this program.  It ought to give the same output as
the previous version.


A FOURTH VARIATION

In the program just tested, the child classes Square and Circle each
inherit from a single parent class, Square, and a single interface,
Sortable.  As already noted, Java allows a child class to inherit from
any number of interfaces but from at most one class.

The fourth variation of the ShapeA program is shown below.  All
changes to the previous version are indicated by comments.

In this version, the data field name, the constructor, and the
non-abstract methods have been removed from class Shape leaving it
with just the two abstract methods perimeter() and area().  In this
cut-down state, class Shape is ripe for conversion to an interface and
that is how it appears.

Given the removal of the data field  name  and the constructor from
Shape, there can no longer be names (like Trafalgar) associated with
individual Shapes.  Additionally, the constructors in the child
classes Square and Circle can no longer include the super(s)
statements.

In consequence, there is only one argument in each of the constructors
of Square and Circle and when the  sa  array is set up the elements
are given as  new Square(2d)  rather than  new Square("Trafalgar",2d)
as before.

The child classes Square and Circle now inherit from TWO interfaces
and an appropriate diagram is:

                            Shape     .Sortable
                            .   .  . .
                           .   . .  .
                          ..      ..
                       Square   Circle

To indicate inheritance from two interfaces the heading line of
class Square is modified from:

   class Square extends Shape implements Sortable

to

   class Square implements Shape, Sortable

Note the use of the comma.  In a more ambitious case a child class may
specify three, four or more interfaces after the  implements  qualifier.

Set up this version now.


public class ShapeA
 { public static void main(String[] args)
    { Sortable[] sa = {new Square(2d),                // no names now
                       new Square(3d),
                       new Circle(1.5d)};
      printOut(sa);
      sort(sa);
      printOut(sa);
    }

   private static void printOut(Sortable[] s)
    { for (int i=0; i<s.length; i++)
         System.out.println("sa[" + i + "]: " + s[i]);
    }

   private static void sort(Sortable[] s)
    { for (int k=1; k<s.length; k++)
       { int i=k;
         while (i>0 && s[i-1].compare(s[i]))
          { Sortable t = s[i-1];
            s[i-1] = s[i];
            s[i] = t;
            i--;
          }
       }
    }
 }

interface Shape                                       // now an interface
 { public abstract double perimeter();                // cut down to just
                                                      // abstract methods
   public abstract double area();
 }

interface Sortable
 { public abstract boolean compare(Sortable that);
 }


class Square implements Shape, Sortable               // implements Shape, Sortable
 { public double side;

   public Square(double side)                         // cut-down constructor
    { this.side = side;
    }

   public double perimeter()
    { return 4d*this.side;
    }

   public double area()
    { return this.side*this.side;
    }

   public boolean compare(Sortable that)
    { return this.side > (that instanceof Square
                          ? ((Square)that).side
                          : ((Circle)that).radius);
    }

   public String toString()
    { return "  Square - " + "\n" +                   //  cut-down first line
             "  Side is " + this.side + "\n" +
             "  Perimeter is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }

class Circle implements Shape, Sortable               // implements Shape, Sortable
 { public double radius;

   public Circle(double radius)                       // cut-down constructor
    { this.radius = radius;
    }

   public double perimeter()
    { return 2d*Math.PI*this.radius;
    }

   public double area()
    { return Math.PI*this.radius*this.radius;
    }

   public boolean compare(Sortable that)
    { return this.radius > (that instanceof Circle
                            ? ((Circle)that).radius
                            : ((Square)that).side);
    }

   public String toString()
    { return "  Circle - " + "\n" +                   //  cut-down first line
             "  Radius is " + this.radius + "\n" +
             "  Circumference is " + this.perimeter() + "\n" +
             "  Area is " + this.area() + "\n";
    }
 }


TRY IT OUT

Compile and run the program. 

Here is the output.  The only difference from before is that there are
no longer any names associated with the Shapes.


sa[0]:   Square - 
  Side is 2.0
  Perimeter is 8.0
  Area is 4.0

sa[1]:   Square - 
  Side is 3.0
  Perimeter is 12.0
  Area is 9.0

sa[2]:   Circle - 
  Radius is 1.5
  Circumference is 9.42477796076938
  Area is 7.0685834705770345

sa[0]:   Circle - 
  Radius is 1.5
  Circumference is 9.42477796076938
  Area is 7.0685834705770345

sa[1]:   Square - 
  Side is 2.0
  Perimeter is 8.0
  Area is 4.0

sa[2]:   Square - 
  Side is 3.0
  Perimeter is 12.0
  Area is 9.0