20-CS-4003-001 Organization of Programming Languages Fall 2018

Lambda calculus, Type theory, Formal semantics, Program analysis

Type Safety, Iterators, and Generic Methods

Instructions for all applets:

All applets contain some buttons at the bottom and a JTextArea in the middle which shows the results of clicking the buttons. All applets are concerned with adding elements to a Vector or PriorityQueue container and one is also concerned with removing elements. The underlying theme of all this is how to support polymorphism, particularly for container classes.


Type safety: The absence of erroneous or undesirable program behavior caused by a discrepancy between differing data types.

Claim: "If your entire application has been compiled without unchecked warnings using javac -source 1.5, it is type safe." - from Oracle

    This applet shows what can go wrong. A Vector can store a mix of objects. But what happens if an object is taken from a container and an operation that is intended for another type of object is applied to it? In this simple example integers and strings representing integers are contained in a Vector. The operation of concern is to append a number which is double the last number (could be a string or int) in the Vector. For the contained integers there is no problem, but when doubling is applied to a string an exception is thrown resulting in a "you're crazy" message being printed. Hit 'Add Int' to add an integer and hit 'Add String' to add a number as a string. Hit 'Double Last' to attempt to double the last number and append it.
    This is a possible fix to the problem of the applet above. Objects stored in the vector are restricted to be of type Integer by the following declaration:
      Vector <Integer> v;
and instantiation
      v = new Vector <Integer> ();
Unfortunately, this is too restrictive and leaves out the possibility of adding string objects to the vector. Operate the buttons as above.
    An interface called I is defined. This interface prototypes a method called dbl which is intended to output a number that is double some unspecified number. Classes A and B implement I. Class A objects hold an integer called number but class B objects hold a String object called str. Each implements dbl differently. Both A and B objects can be stored in the vector because it is declared and instantiated like this:
         Vector <I> v;
         v = new Vector <I> ();
Operate the buttons as above. The applet does what is expected.
    There are no unchecked warning when this compiles. Yet clicking on 'Double Last' fails when the last element is a B object because the last element is allowed to be cast to an A object then a method of class A that has no counterpart in class B is invoked. This appears to show that Java is not type safe.
    Some containers need an auxilliary function such as a Comparator. An example is the PriorityQueue. This example stores the same A and B objects as above. The Comparator is defined for interface I which is modified to require function val which is used to compare I objects. Classes A and B implement val accordingly.

This example also illustrates the use of an Iterator to move through a collection of objects (see the display) function.

    A function display prints the contents of any Collection. It uses the wildcard <?> to say that any Collection subtype is allowed as an argument to display. The following as argument to display do not compile:
  (I w) - cannot find iterator method
  (AbstractCollection <I> w) - linked list is not included
  (AbstractCollection <I,S> w) - wrong number of arguments
The following compile without displaying an unchecked warning and run correctly - do not ask me why - maybe a bug?
  (AbstractCollection w)
  (Collection w)
    Classes UA and UB subclass U. Let v be a Vector of type UA and l be a LinkedList of type UB. It is desired to display UA and UB containers. But if display is prototyped like this:
  public String display(Collection <U> w) 
then code that uses display(v) or display(l) won't compile. The solution is:
  public String display(Collection <? extends U> w) 
    The method arrayToCollection is a generic method. Trying to write
  public void arrayToCollection (Object [] a, Collection <?> b) {
     for (int i=0 ; i < a.length ; i++) b.add(a[i]);
does not compile - there is a type mismatch between <?> and Object. So the following is used
  public <T> Collection <T> arrayToCollection (T [] a, Collection <T> b) {
     for (int i=0 ; i < a.length ; i++) b.add(a[i]);
     return b;
where a return value was added for fun. Now the types match. The <T> signifies that this is a generic method and specifies a type that is used in the parameter list.
    Three ways to protoype display are shown. These are
   public String display1 (Collection <?> w)
   public <T> String display2 (Collection <T> w)
   public <T extends I> String display3 (Collection <T> w)
The first form is used to support flexible sybtyping (polymorphism) which is the case in this example. The 2nd and 3rd forms are used when dependencies exist between the types of the parameters as was the case in the previous example.
    The following are defined:
  class DoNothing <T> {  boolean neverMind(T t) { return true; };  }
  public <.1.> T write (Collection <.2.> c, DoNothing <.3.> dn) { ... }
where write is intended to apply neverMind to all elements of collection c, and then output the last element of c. In this example write is used like this:
  DoNothing <Object> s = new DoNothing <Object> ();
  Collection <String> cs = new Vector <String> ();
  String str = write(cs, s);
The question is what should <.1.>, <.2.>, <.3.> be? Our first thought might be to write this:
  public <T> T write_1 (Collection <T> c, DoNothing <T> dn)
But this does not work since T is inferred by the compiler to be String from the second line above, but also Object from the first line above. So there is a type mismatch in the parameters. Our second thought might then be to do this:
  public <T> T write_1 (Collection <? extends T> c, DoNothing <T> dn)
because ? will be inferred to be String and, after all, String extends Object, which T is inferred to be. But this fails because the last line in the code above tries to assign an Object (output of write) to a String. The solution is to write:
  public <T> T write_1 (Collection <T> c, DoNothing <? super T> dn)
Now ? is inferred to be Object and T is inferred to be String as needed.
    Using generics helps cut down on casting as this example shows.
    Example showing multiple parameter types for a generic class. Note that some types are inferred in class MH2 but the type for n in makeKeys is not, so a cast is necessary.



    Generics tutorial from Oracle