Lectures
Lecture 1: The Essence of Objects
Lecture 2: Unions of Objects
Lecture 3: Cancelled
Lecture 4: Classes of Objects:   Data Definitions
Lecture 5: Classes of Objects:   Interface Definitions
Lecture 6: Interface Design:   Independent and Extensible
Lecture 7: Parametric Interface Definitions and Methods
Lecture 8: Introducing Java:   Syntax and Semantics
Lecture 9: Union, Interfaces, and Lists in Java
Lecture 10: Testing in Java
Lecture 11: Parametric Interfaces in Java
Lecture 12: Computations on Many Structural Arguments:   Double Dispatch
Lecture 13: Parameterized Types and Double Dispatch; Abstracting Values
Lecture 14: Abstracting Computation with Function Objects
Lecture 15: Function Objects & Parameterized Types; Anonymous Classes & Lambda
Lecture 16: The Fundamental List Abstraction:   Fold
Lecture 17: University Closed:   Wind
Lecture 18: Midterm Review
Lecture 19: Properties of Equality:   Reflexive, Symmetric, Transitive, and Total
Lecture 20: Structural Equality with Double Dispatch; Abstracting and Overridding
Lecture 21: More Double Dispatch
Lecture 22: Optional, Maps, Sets, and Lifting Default Code to Abstract Classes
Lecture 23: The Visitor Pattern
Lecture 24: Implementing Visitors; Bank Accounts
Lecture 25: Imperatives:   Implicit Communication via Side-Effects
Lecture 26: Aside:   List Exercises
Lecture 27: Imperatives:   Cyclic Data
Lecture 28: Imperatives:   Methods over Cylic Data
Lecture 29: BSTs, Maps, The Law of Hash  Code, and Comparable vs Comparators
Lecture 30: Random access and Array  Lists
Lecture 31: Implementing Hash Tables
Lecture 32: Resizing Hash Tables
Lecture 33: Simple Iterators
Lecture 34: List Iterators and Iterator Combinators
Lecture 35: List Iterators and Iterator Combinators
Lecture 36: Zippers
Lecture 37: Naive Tree Iterators
Lecture 38: Efficient Pre-Order Tree Iterators
Lecture 39: Drills
Lecture 40: Drill Solutions
Lecture 41: Wrap-up
On this page:
1 Definitions
2 Concepts
3 Hash code
4 Comparisons
5 Abstract classes
6 Types
7 Tree traversals
8 Computing with iteration
9 Lambda expressions
10 Methods for lists
11 Double dispatch
12 Graphs
13 Visitors
6.12

Lecture 39: Drills

1 Definitions

Give a brief definition of
  • interface

  • class

  • object

  • abstract class

  • iterator

  • comparator

  • anonymous inner class

  • lambda expression

  • visitor

2 Concepts

Describe two ways in which an abstract class and a class differ.

What does it mean when
  • a class implements an interface

  • a class extends a class

  • an interface extends an interface

3 Hash code

If two objects have the same hash code, what can you conclude about the equality of those objects (assuming their equals and hashCode methods are correct)?

4 Comparisons

What’s the difference between a comparator and comparable object?

5 Abstract classes

Describe a situation in which writing an abstract class is justified.

6 Types

Is this program well-typed?

Listof<Integer> is = new Cons <>(5, new Empty<>());

System.out.println(is.first);

Is this program well-typed?

Pairof<Integer, Boolean> p = null;

System.out.println(p.left);

7 Tree traversals

Suppose you had a binary search tree and you want to produce a list of elements in the tree in sorted order. Which traversal do you want?

8 Computing with iteration

Implement the following method:

// Compute the average of all the numbers in it.

// Assume: it has at least one number

Double avg(Iterable<Double> it) { ... }

9 Lambda expressions

Rewrite the following to use lambda expression notation:

Function<Double,Double> dbl = new Function<>() {

    public Double apply(Double d) {

        return d * d;

    }

};

10 Methods for lists

Assume the following interface exists:

// Functions X -> Optional<Y>

interface OptFun<X, Y> extends Function<X, Optional<Y>> {}

Design the following methods for Listof<X>:

// Produce the list of elements in this list which satisfy p

Listof<X> filter(Predicate<X> p);

 

// Produce the list of results for which f produces something in this list

<R> Listof<R> filterMap(OptFun<X,R> f);

 

// Produce the first element in this list for which p produces true

Optional<X> findPred(Predicate<X> p);

Show that you can define filter in terms of filterMap:

abstract class AListof<X> implements Listof<X> {

  // Produce the list of elements in this list which satisfy p

  public Listof<X> filter(Predicate<X> p) {

    ...

  }

}

11 Double dispatch

When is it justified to use the double dispatch pattern?

Assume the following definitions:

interface BT<X> {

  // Produce the tree obtained by following p in this tree

  BT<X> follow(BTPath p);

}

 

class Leaf<X> implements BT<X> { ...usual defn...}

 

class Node<X> implements BT<X> { ...usual defn...}

 

// Interp: a path through a binary tree

interface BTPath {}

 

// Interp: end of a path

class End implements BTPath {}

 

// Interp: go left, followed by path rest

class Left implements BTPath {

    BTPath rest;

    Left(BTPath rest) {

        this.rest = rest;

    }

}

 

// Interp: go right, followed by path rest

class Right implements BTPath {

    BTPath rest;

    Right(BTPath rest) {

        this.rest = rest;

    }

}

Using the double dispatch pattern, design the follow method. You may raise an exception if following a path leads you off the end of a binary tree.

12 Graphs

Here is a representation for directed graphs:

// A node in a directed graph

class GNode {

    String name;

    Listof<GNode> neighbors;

 

    GNode(String name) {

        this.name = name;

        this.neighbors = new Empty<>();

    }

 

    // EFFECT: add the given node to this node's neighbor

    public void addNeighbor(GNode n) {

        this.neighbors = new Cons<>(n, this.neighbors);

    }

 

    // Is there a path from this node to the given node?

    public Boolean hasPath(GNode to) { ... }

}

Design the hasPath method. Here some examples:

void testGraph(Tester t) {

    // +---------+

    // v         |

    // A--->B--->C-->D

    //      +--->E-->F

    //

    GNode a = new GNode("A");

    GNode b = new GNode("B");

    GNode c = new GNode("C");

    GNode d = new GNode("D");

    GNode e = new GNode("E");

    GNode f = new GNode("F");

 

    a.addNeighbor(b);

    b.addNeighbor(c);

    c.addNeighbor(d);

    b.addNeighbor(e);

    e.addNeighbor(f);

    c.addNeighbor(a);

 

    t.checkExpect(a.hasPath(a), true);

    t.checkExpect(a.hasPath(b), true);

    t.checkExpect(a.hasPath(e), true);

    t.checkExpect(e.hasPath(d), false);

}

13 Visitors

Recall the following method for Listof<X>:

// Is this list sorted in ascending order according to c?

Boolean isSorted(Comparator<X> c);

It can be implemented with an accumulator helper method that remembers the prior element in the list.

Suppose this method was not available for Listof<X> and you couldn’t make any further changes to Listof<X>. Design a visitor that computes the same thing as this method.