CS136, Lecture 36

  1. Impossible problems
  2. Evaluation of Object-Oriented Languages and Java
    1. Advantages:
    2. Flaws in today's OOL's in general:
    3. Biggest flaws in Java:
      1. Extending Java
    4. Generics
  3. Wrapping it all up
    1. Goals of the course:

Impossible problems

or

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:

  1. Suppose halts("Debunk.java") returns true (meaning the program in Debunk.java will eventually halt). Then the program goes into an infinite loop, never halting. Thus the program in Debunk.java will never halt. CONTRADICTION!

  2. Suppose halts("Debunk.java") returns true (meaning the program in Debunk.java will not halt). Then the program immediately halts. Thus the program in Debunk.java will eventually halt. CONTRADICTION

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.

Evaluation of Object-Oriented Languages and Java

Advantages:

1. An object keeps track of its own operations. All operations are grouped with the data definition.

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!

Flaws in today's OOL's in general:

1. Code for methods and instance variables of objects can be spread through several files (e.g., superclasses), making it hard to find code.

2. Because all methods are implicitly mutually recursive, overriding one method in a subclass may produce surprising results.


Biggest flaws in Java:

1. Current compilers are slow, generated code also slow. Will be fixed in time

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.

Extending Java

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

Generics

Provide "constrained genericity".

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.

Wrapping it all up

Goals of the course:

  1. to introduce you to the basics of data structures and algorithm analysis. We emphasized the use of abstraction in designing classes. These techniques support the design of powerful new data structures and operations on them which can be treated as though they are primitive.

  2. to introduce you to the object-oriented programming paradigm, which supports powerful and useful ways of thinking about and designing programs:
    classes, objects with instance variables and methods, inheritance and subtyping.

  3. to introduce you to the interactive programming with the event-driven style of graphic user interfaces.

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.