Relevant Reading Material

Chapters From How to Design Classes (HtDC)

  • Chapter 4

  • Chapter 5

  • Chapter 6

  • Intermezzo 1

  • Chapter 8

  • Chapter 9

  • Chapter 10

  • Chapter 11

Evaluation order DrRacket and Java

In DrRacket we had a set of rules that dictate how an expression gets evaluated. We used the Stepper in order to visualize these steps for an expression under evaluation.

Java has its own evaluation rules. We will start with core set of evaluation rules and add to these as the class progresses. All popular IDEs also provide a debugger that allows us to step through the execution of a Java program. For now you can think of a debugger to be similar to the DrRacket stepper. As we progress through the course we’ll see more on debugging and some of the difference between a debugger and the DrRacket Stepper.

Itemizations

Let’s consider the design of a chess game. We would like to capture the pieces of a chess board and the chess board as data. Our chess game will also have a graphical user interfaces so we need to capture any information that we might need in order to draw the pieces on our chessboard.

We will focus on the information for chess pieces first excluding movement.

Chess has the following pieces

  • Pawn

  • Rook

  • Knight

  • Bishop

  • Queen and

  • King

Each player is given a collection of these pieces. The collection consists of

  • 8 Pawns

  • 2 Rooks

  • 2 Knights

  • 2 Bishops

  • 1 Queen and

  • 1 King

The two collections have different colours, one player has white and the other black.

One possible way to capture these pieces of information in Racket could be the following

;; A Colour is one of
;; - 'white
;; - 'black

;; A Piece is one of
;; - Pawn
;; - Rook
;; - Knight
;; - Bishop
;; - Queen
;; - King


(define-struct pawn (loc colour))
;; A Pawn is (make-pawn Posn Colour)
;; INTERP: represents a pawn with its colour and location

(define-struct rook (loc colour))
;; A Rook is (make-rook Posn Colour)
;; INTERP: represents a rook with its colour and location

(define-struct knight (loc colour))
;; A Knight is (make-knight Posn Colour)
;; INTERP: represents a knight with its colour and location

(define-struct bishop (loc colour))
;; A Bishop is (make-bishop Posn Colour)
;; INTERP: represents a bishop with its colour and location

(define-struct queen (loc colour))
;; A Queen is (make-queen Posn Colour)
;; INTERP: represents a queen with its colour and location

(define-struct king (loc colour))
;; A King is (make-king Posn Colour)
;; INTERP: represents a king with its colour and location

In Racket a union Data Definition grouped together data that served a specific role in our design. The name of the union Data Definition was used in our signatures and Data Definitions but did not really have a presence in the source code.

Interfaces in Java

Java provides Interfaces. A Java Interface is much like a class but

  • There is no constructor! We cannot directly create an instance of an interface.

  • Everything in the interface is public.

  • There are no fields in interfaces.

  • You can only have method signatures in interfaces. [1]

  • A class implements an interface by explicitly declaring in its class header the name of the interface using the special keyword implements. A class can implement as many interfaces as it wants.

  • Any class that claims to implement an interface must provide code for each signature in the interface.

Think of an interface as a way to provide a view on your objects that shows only the relevant information for that view on this object.

Back to our chess game now, we will capture itemizations in Java as interfaces and thus Piece is going to be an interface. Each of the itemization elements of the data definition, i.e., Pawn, Rook etc. will implement our interface, i.e., Piece

piece union

Here is some of the code Rook

Colour.java
/**
 * Represents the colour of a pieces in a Chess game.
 */
public interface Colour {
}
White.java
/**
 * Created by therapon on 5/16/16.
 */
public class White implements Colour {
}
Black.java
/**
 * Created by therapon on 5/16/16.
 */
public class Black implements Colour {

}
Piece.java
/**
 * Represents a piece in our chess game.
 *
 */
public interface Piece {
}
Pawn.java
/**
 * Created by therapon on 5/16/16.
 */
public class Pawn implements Piece {

    private Posn loc;
    private Colour colour;

    public Pawn(Posn loc, Colour colour) {
        this.colour = colour;
        this.loc = loc;
    }

    /**
     * Getter for property 'loc'.
     *
     * @return Value for property 'loc'.
     */
    public Posn getLoc() {
        return this.loc;
    }

    /**
     * Getter for property 'colour'.
     *
     * @return Value for property 'colour'.
     */
    public Colour getColour() {
        return this.colour;
    }
}
Rook.java
/**
 * Created by therapon on 5/16/16.
 */
public class Rook implements Piece {

    private Posn loc;
    private Colour colour;

    public Rook(Posn loc, Colour colour) {
        this.loc = loc;
        this.colour = colour;
    }

    /**
     * Getter for property 'loc'.
     *
     * @return Value for property 'loc'.
     */
    public Posn getLoc() {
        return this.loc;
    }

    /**
     * Getter for property 'colour'.
     *
     * @return Value for property 'colour'.
     */
    public Colour getColour() {
        return this.colour;
    }
}

And here is a test for Pawn. We will add a test to verify Colour soon.

PawnTest.java
import org.junit.Assert;

public class PawnTest {
    private Pawn p1;

    @org.junit.Before
    public void setUp() throws Exception {
        this.p1 = new Pawn(new Posn(1,1), new White());
    }

    @org.junit.Test
    public void getLoc() throws Exception {
        Assert.assertTrue(samePosn(this.p1.getLoc(), new Posn(1,1)));

    }

    private Boolean samePosn(Posn loc, Posn posn) {
        return loc.getX().equals(posn.getX()) && loc.getY().equals(posn.getY());

    }

}

A Java interface is a valid type for a field/variable. The possible values that we can set to a field/variable whose type is the name of an Interface are all the instances of classes that implement that interface.

PieceTest.java
import org.junit.Before;
import org.junit.Test;


public class PieceTest {

    private Piece pawn;
    private Piece rook;
    private Piece bishop;
    private Piece knight;
    private Piece queen;
    private Piece king;
    private Posn p1;
    private Colour black;
    private Colour white;

    @Before
    public void setUp() throws Exception{
        this.p1 = new Posn(1, 1);
        this.black = new Black();
        this.white = new White();

        this.pawn = new Pawn(this.p1, this.black);    (1)
        this.rook = new Rook(this.p1, this.white);
        this.bishop = new Bishop(this.p1, this.black);
        this.knight = new Knight(this.p1, this.white);
        this.queen = new Queen(this.p1, this.black);
        this.king = new King(this.p1, this.white);
    }

    @Test
    public void testPiece(){
        this.pawn.getLoc();      (2)
    }
}
1 Observe that the instance we are creating on the right hand side of the = is a Pawn while the field we are assigning to this.pawn was given the type Piece. Given that Pawn implements Piece this is a valid assignment.
2 This line gives a compile time error
PieceTest.java:30: error: cannot find symbol
        this.pawn.getLoc();
                 ^
  symbol:   method getLoc()
  location: variable pawn of type Piece

The compiler complains that there is no getLoc() method defined for the field pawn which has the type Piece. The reason is that Piece as an interface provides no method signatures. Even though we know that we assigned the value new Pawn(this.p1, this.black) to the field pawn and that Pawn does have a method called getLoc the compiler only checks against the code in the interface Piece in order to find a method getLoc and not against Pawn. The compiler only considers information that is available at compile time.

Adding methods on our Piece Itemization

Recall in Racket our functions took as input a value of our itemization and then using a cond statement detected which one of the valid values for this itemization we have and delegated to the appropriate function.

For example if we wanted to have get the location of an Piece

;;;; Signature
;; piece-posn: Piece -> Posn
;;;; Purpose
;; Given a piece returns the piece's location

(define (piece-posn piece)
  (cond
    [(pawn? piece) (pawn-loc piece)]
    [(rook? piece) (rook-loc piece)]
    [(knight? piece) (knight-loc piece)]
    [(bishop? piece) (bishop-loc piece)]
    [(queen? piece) (queen-loc piece)]
    [(king? piece) (king-loc piece)]))

With Java since we captured our itemization as an Interface we need to

  1. add a signature to the interface that captures our itemization and

  2. to each class that maps to an element of the itemization provide an implementation of this signature

In order to be use the methods getLoc and getColour we need to

  1. Add the signatures of these methods in Piece

  2. Ensure that all classes that implement Piece provide an implementation for each signature in Piece.

    1. The signatures have to match exactly!

Let’s update Piece to add the signatures that we want to use for all Piece instances.

Piece.java
/**
 * Represents a piece in our chess game.
 *
 */
public interface Piece {

    /**
     * Get the location of this piece
     * @return  the current location of this piece
     */
    Posn getLoc();

    /**
     * Get the colour of this piece
     * @return this piece's colour
     */
    Colour getColour();
}

Another way to think about how the Racket implementation maps to the Java implementation is that the cond is done by Java during evaluation, and we need to add the corresponding Java code that maps to the right hand side of the cond -clause to the appropriate class.

And here is our updated UML Class Diagram

piece union interface sigs

Now our code compiles!

Java Evaluation

Whiteboard!

Abstracting commonalities using inheritance

Observe that each of the classes that implement Piece have two fields

  1. loc

  2. colour

Java provides a mechanism called inheritance that allows classes to inherit propertied from their parents.

Inheritance in Java is a relationship between classes. The meaning of inheritance in Java is close to the meaning of the word inheritance in the English in that one class derives characteristics from another.

In Java the characteristics that are derived are

  1. public fields

  2. public methods.

private characteristics (methods/fields) of a class are not inherited.

So we could create a new class that will contains the common fields and methods across all classes that implement Piece. The purpose of this class is to contain all the common fields and methods across pieces. It is an abstraction (think of it as an incomplete segment) that we would like all concrete Piece implementation to share. As such we do not want to create instances of this incomplete segment.

Java allows us to define classes that are abstract in nature and cannot be instantiated.

AbstractPiece.java
/**
 * Created by therapon on 5/16/16.
 */
public abstract class AbstractPiece {        (1)

    protected Posn loc;             (2)
    protected Colour colour;

    public AbstractPiece(Posn loc, Colour colour) {    (3)
        this.colour = colour;
        this.loc = loc;
    }

    /**
     * Getter for property 'loc'.
     *
     * @return Value for property 'loc'.
     */
    public Posn getLoc() {
        return this.loc;
    }

    /**
     * Getter for property 'colour'.
     *
     * @return Value for property 'colour'.
     */
    public Colour getColour() {
        return this.colour;
    }

}
1 An abstract class is declared by using the keyword abstract in the class definition header
2 protected is another modifier like public and private. We can use protected for fields and/or methods. A field/method that is marked as protected designates that
3 Abstract classes can have constructors inside their definition. These constructors cannot be used to create an instance of the abstract class. They are used by subclasses to create the segments of the abstract class that they inherit and then fill in the remaining fields (if there are any).

By declaring a class abstract Java will not allow us to create instance of the class directly, e.g., you cannot use new on an abstract class. It is essentially "placeholder" with some common behaviour and/or some method signatures that we want all subclasses to inherit and/or implement.

Here is the updated implementation of Queen

Queen.java
/**
 * Created by therapon on 5/16/16.
 */
public class Queen extends AbstractPiece implements Piece {  (1)


    public Queen(Posn loc, Colour colour) {
        super(loc,colour);              (2)
    }


}
1 To define an inheritance relationship to another class we need to specify our parent class in the class definition header using the keyword extends.
2 In the constructor we can use the keyword super to refer to our parent class' constructor. We can also use super to refer to one of our parents methods, e.g., super.getLoc() calls the method in our parent class which is AbstractPiece.
In Java a class can only extend one other class and no more!

And here is our update UML Class Diagram

piece union interface absclass

Given that all the code that implements the signatures found in Piece are in AbstractPieces we could move the implements Piece declaration to AbstractPiece and all of our classes that extend AbstractPiece will still implement Piece.

AbstractPiece.java
/**
 * Created by therapon on 5/16/16.
 */
public abstract class AbstractPiece implements Piece {        (1)

    protected Posn loc;
    protected Colour colour;

    public AbstractPiece(Posn loc, Colour colour) {
        this.colour = colour;
        this.loc = loc;
    }

    /**
     *      * Getter for property 'loc'.
     *           *
     *                * @return Value for property 'loc'.
     *                     */
    public Posn getLoc() {
        return loc;
    }

    /**
     *      * Getter for property 'colour'.
     *           *
     *                * @return Value for property 'colour'.
     *                     */
    public Colour getColour() {
        return colour;
    }

}
1 AbstractPiece now implements Piece and through inheritance so will all the classes that extends AbstractPiece. Notice that AbstractPiece does not provide implementations for the signatures defined in Piece. We will talk about abstract classes extensively in our next lecture.
King.java
/**
 * Created by therapon on 5/16/16.
 */
public class King extends AbstractPiece  {


    public King(Posn loc, Colour colour) {
        super(loc, colour);
    }

}

And our updated UML Class Diagram again

piece union interface inh abs

Java Evaluation again

Whiteboard!

Shapes Example

Let’s try another example. We would like to design a program that allows us to manipulate shapes on a canvas.

A Shape, for now, can be one of

  • a circle with a radius and a pin (its center)

  • a square with a pin (the top left corner) and a side

  • a rectangle with a pin (the top left corner) a width and a height

This is an example of an itemization.

We can make some observations about the data for each shape.

  1. There are elements of each shape that are common, the pin.

  2. There are element of each shape that are uncommon (or special) to each individual shape, e.g., radius for circle

Lets look at the operations that we want for each shape.

  1. We would like to move a shape on the canvas in the x-direction

  2. We would like to move a shape on the canvas in the y-direction

  3. We would like to calculate the area of a shape

  4. We would like to calculate the circumference of a shape

In Racket

Recall what we had to do for itemizations in Racket?

;; A Shape is one of
;; - (make-circle Posn PosInt)
;; - (make-square Posn PosInt)
;; - (make-rectangle Posn PosInt PosInt)


;; shape-area: Shape -> Number
(define (shape-area shape)
  (cond
    [(circle? shape) (circle-area shape)]
    [(square? shape) (square-area shape)]
    [(rectangle? shape) (rectangle-area shape)]))


;; circle-area: Circle -> Number
(define (circle-area circle)
   (* pi (sqr (circle-radius))))

;; square-area: Square -> Number
(define (square-area square)
  (sqr (square-side square)))


;; rectangle-area: Rectangle -> Number
(define (rectangle-area rectangle)
  (* (rectangle-width rectangle)
     (rectangle-height rectangle)))

In Java

We can use inheritance for the common parts of our data so that we define a pin in one location in our code and have all the specific shapes that use a pin inherit that field.

shape

Fields and constructors in the presence of inheritance

The class Circle has access to radius but it also has access to pin. The same argument goes for each subclass.

The Java language provides syntax to refer to you parent. In Java the special keyword super refers to your parent.

In order to create a new Circle instance we need to provide a constructor method like before. Now however we need to create our constructor to accept a value for radius and a value for pin.

public abstract class AbstractShape implements Shape {
    protected Posn pin;

    public Shape(Posn pin) {
        this.point = point;
    }
}

public class Circle extends AbstractShape { (1)
    private Integer radius;

    public Circle(Posn pin, Integer radius) {
        super(pin);
        this.radius = radius;
    }
}
1 The Java keyword extends specifies that Circle inherits from Shape. We say that Circle is a subclass of Shape and Shape is the superclass of Circle.

In Java you can only have 1 superclass. This property is called single inheritance. A superclass however can have more than one subclasses.

The statement super(pin) tells Java to call the parent (or superclass) constructor in Shape passing pin as the argument to Shape 's constructor. This will setup pin correctly and then we proceed to set the fields found in Circle.

In Java calling the superclass constructor must be the first line in the subclass constructor.

Methods in the presence of inheritance

Java applies similar rules to methods when it comes to inheritance and which methods are derived or not.

A subclass inherits all public and protected methods that are present in the superclass.

A subclass can

  1. Use the inherit method as is and call it on itself.

  2. It can override the method in order to provide it’s own implementation. An override however must satisfy the following conditions

    1. The method signature must be the same

    2. Use the @Override annotation.

If you would like to call the overriden method, Java allows you to do so with super.<method-name>(<args>) where

  • <method-name> is the name of the method you want to call in the superclass.

  • <args> are any number of arguments (0 or more) that you want to pass to the superclass' method you are calling.

Inheritance, types and polymorphism

Inheritance defines a relationship between a superclass and a subclass. This relationship affects how the Java compiler and the JVM treat instances of these classes.

Given that a subclass must have all the publicly available methods of its superclass (overridden or not) we can view a subclass as its superclass.

 Circle c1 = new Circle(new Point(10,20), 5);

The above line is valid Java code. We create a variable called c1 that can hold objects of type Circle and we assigned to c1 the object new Circle(new Point(10,20), 5).

 Shape s1 = new Circle(new Point(10,20), 5);
 AbstractShape s2 = new Circle(new Point(10,20), 5);

The preceding line is also valid Java. We create a new variable called s1 that can hold objects of type Shape and we assigned to s1 the object new Circle(new Point(10,20), 5). Some terminology here we say

  • the compile time type (or static type) of s1 is Shape

  • the runtime type (or dynamic type) of s1 is Circle.

  • the compile time type (or static type) of s2 is AbstractShape

  • the runtime type (or dynamic type) of s2 is Circle.

Conceptually you can think of this as, given that a Circle has the same methods and fields as a Shape (well maybe a bit more) then it is perfectly valid to use it a Shape if I want to.

The Java compiler will treat s1 as a Shape in your code and will not allow you to call a method on s1 that is not found in the Shape interface or one of its superclasses. Superclasses here refers to the parent, grandparent, grand-grandparent etc.

The Java compiler will treat c1 as a Circle in your code and will not allow you to call a method on c1 that is not found in the Circle class or one of its superclasses. Superclasses here refers to the parent, grandparent, grand-grandparent etc.

The JVM however has the following evaluation rule :

  • when looking up a method on an instance o that has runtime type T

    1. look at the class T and see if the method is defined in class T, override or not. If the method is defined in T run it on o

    2. else if the method is not defined in T, grab T 's immediate superclass (lets called that S1), if the method is defined in S1, override or not, run it on o

    3. else if the method is not defined is S1, grab S1 's immediate superclass (lets called that S2), if …​

    4. we terminate once we reach the class Object. If Object does not have the method then we throw an error. This error should never happen in a Java program that was build using the Java compiler.

The ability of one instance to be viewed/used as different types is called polymorphism — the ability to takes many "shapes"/"forms".

In Java the cond is taken care of by the language due to the way we designed our classes. What we have to write is the helper functions of for shape-area and we have to write them inside the appropriate class.

public class Circle extends AbstractShape{
    // elided code

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

}

public class Square extends AbstractShape{
    // elided code

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

}

public class Rectangle extends AbstractShape{
    // elided code

    public Double area() {
        return this.width * this.height;
    }
}



public class ShapeTest {

    private Shape c1;
    private Shape s1;
    private Shape r1;

    @Before
    public void setUp() throws Exception {
        this.c1 = new Circle(new Point(10,20), 5);
        this.s1 = new Square(new Point(100,100), 20);
        this.r1 = new Rectangle(new Point(300,300), 30, 10);
    }


    @Test
    public void testArea() {
        Assert.assertEqual(this.c1.area(), new Double(78.53981633974483));
        Assert.assertEqual(this.s1.area(), new Double(400));
        Assert.assertEqual(this.r1.area(), new Double(300));
    }
}

Equality in Java

TBD

1. Java 8 removes this restriction and we will learn more about interfaces later in the course