Have I Got a DEAL for You!
Suppose someone wants to sell you a program that will enable you to stop wasting lots of time in your programs. They will sell you a program that will analyze any other program (before you run it) and tell you whether or not it will stop. Should you buy the program (or try to write it yourself)?
You try out the program on lots of your programs and it seems to work properly. Then you get the inspiration to try it out on one last program. Here is the program:
public class Debunker { /* CharlatanType contains method halts, which takes a filename as input and returns true iff the program contained in that file is a legal Pascal program that halts on empty input. */ public static void what(String fileName) { CharlatanType charlatan = new CharlatanType(); if (charlatan.halts(FileName)) { while (true){} } // else halt } public static void main(String[] args) { what("Debunk.java"); } }
Following the usual style in Java, we name the file in which this program is stored, Debunk.java.
We begin executing the program Debunk, but in a few moments smoke starts rising from the computer, and after a few more minutes the machine grinds to a halt showing the frowning Mac face, never to run a program again.
What happened?
Let's analyze the program:
When what("Debunk.java") is executed, the first thing that happens is that Halts("Debunk.java") is called.
It will either return true or false. Look at the cases:
What went wrong? The only possibility is that we couldn't possibly have such a program! In other words, without even looking at the code of halts, we have proved that it could not possibly work the way it claimed to. Thus no one could write such a program.
There are many such programs that would be useful to have that are not possible to write. This is discussed further in CS361, Theory of Computation.
2. It provides features to support reusability: primarily subtyping and inheritance.
a. Subtyping is the ability to use an object of the subtype whereever you expected an object of the supertype.
b. inheritance is a way of sharing the code (or implementation) of instance variables and methods from the superclass.
Notice that these are different! Take advantage of this later!
2. Because all methods are implicitly mutually recursive, overriding one method in a subclass may produce surprising results.
2. It has a very inflexible type system (as does C++). You can't change the type of instance variables or of methods in subclasses.
Why is Java (and C++) so rigid in not allowing changes to types of instance variables and methods?
Suppose we have a class A with field x of type Point and method init defined as follows:
public class A { ... Point x; public void init() { Point p; p = new Point(); p.set_coords(0,0); x = p; } ... }
Now define
public class B extends A { ColorPoint x; // suppose this changed type of x from A }The method init no longer type-checks since we assign a Point to a field expecting a ColorPoint!
(The other direction would be fine, but we can't assign a supertype value to a subtype variable.)
This explains the rigidity of rules on changing types of instance variables.
Similar reasons are behind the restriction on using subtypes in place of var parameters.
Java needs type parameters:
public class Stack <T> { T[] data; public Stack(int n){ data = new T[n]; } public void push(T value){...} ... }
In some circumstances, it is OK (and extremely helpful) to change the type of instance variables (and methods) if you do it consistently.
The key is to think about the use of "this" and its type
-- we
will call its type, "ThisType."
Let's go back to SinglyLinkedListElement and DoublyLinkedListElement .
public class SinglyLinkedListElement <T>{ protected T data; // value stored in elt protected ThisType nextElement; // ref to next element public SinglyLinkedListElement(Object v, ThisType next) // post: constructs a new element with value v, // followed by next element { data = v; nextElement = next; } public ThisType next() // post: returns reference to next value in list { return nextElement; } public void setNext(ThisType next) // post: sets reference to new next value { nextElement = next; } ... }
Now define:
class DoublyLinkedListElement { protected ThisType previousElement; public DoublyLinkedListElement(Object v,ThisType next,ThisType previous) // post: constructs new element with list // prefix referenced by previous and // suffix reference by next { super(v,next); previousElement = previous; if (previousElement != null) previousElement.nextElement = this; } public ThisType previous() // post: returns element that precedes this { return previousElement; } ... }
Now ThisType stands for DoublyLinkedListElement. Notice that old methods do not break (and their types change accordingly to reflect ThisType = DoublyLinkedListElement.
This will work as long as in type-checking methods, we only assume that MyType refers to some subclass of SinglyLinkedListElement.
Normally we cannot change the types of instance variables or replace the type of a parameter by its subtype!
Result is that DoublyLinkedListElement cannot be used as subtype of SinglyLinkedListElement.
Recall Object has method:
public boolean equals(Object other)Could now write
public boolean equals(ThisType other)Then any class inheriting Object would have equals which takes parameters of same type.
Similarly, have
public ThisType clone()
Define
public interface Comparable{ public boolean less_than(ThisType other) public boolean equals(ThisType other) }
public interface SortInterface <T matching Comparable> { public void sort (T[] elts); }
Now SortInterface can be instantiated with any type T which matches Comparable - i.e., that contains less_than and equals methods - just what we needed.
Exam: 2 1/2 hours. Comprehensive with extra attention to topics since last exam:
Graphs, Dictionary, Concurrency.
Questions will be similar to those of midterm exams.