III. Generic Types

    A. Polymorphism: refers to the ability of the same code to perform the
        same action on different types of data. There are two primary
	types of polymorphism:

	1. Parametric polymorphism: The code takes the type as a parameter,
	    either explicitly (as in C++/Java templates) or implicitly (as in
	    Lisp, which determines the type from the data being used). Some
	    compilers, such as the C++ compiler, generate multiple copies of 
	    the code, one for each type. Other compilers, such as the Java
	    compiler, generate a single copy of the code which should work
	    with all types. This is why Java's templates cannot support
	    primitive types, because a single copy of the code cannot use
	    built-in operations, such as + or <. Instead it must rely
	    on method calls, and hence it must work with objects.

	2. Inheritance polymorphism: The code is shared between the super
	    class and its children via a non-virtual method. Hence
	    the same code works with multiple objects of different types, but
	    the types all share a common supertype.

   B. Polymorphism in C, C++, and Java
       1. Java
           a. Original approach: the developers of Java felt that C++ templates
               were too complicated, so their original solution was to provide 
               the implicit master class Object. 

               i. Generic code uses the type Object whenever it needs to refer 
	          to a specific piece of data

	       ii. Programs downcast the Object type to a specific type when they
	           retrieve an object from a generic data structure

	       iii. Disadvantages

	            1. downcasting can be dangerous

	            2. Object supports generic data structures but not generic
	               algorithms, such as quicksort, which require a common
		       comparator method

                iv. Advantages: One copy of the code works with multiple types

             b. All this downcasting was both bothersome to use, and ran the
                risk of run-time type conversion errors that could not be
                type-checked by the compiler. Eventually the Java developers
                introduced a simplified template mechanism. When programmers
                declare a variable to use a template type, the declaration looks
                much like a C++ template. However, the definition of a template
                type is simpler. Here's a simple declaration of a wrapper
                type:

                public class Box<T> {

                    // T stands for "Type"
                    private T t;

                    public void add(T t) {
                        this.t = t;
                    }

                    public T get() {
                        return t;
                    }
                }

                As you can see, the type looks like a parameter to a 
                function. You are allowed to pass multiple types to a
                template. For example, a hash table would have types for
                both the key and the value objects.

                Here is a declaration of a variable of type Box, and its
                use:

                class Test {
                    static public void main(String args[]) {
                        Box<String> b = new Box<String>();
                        b.add(args[0]);
                        String myArg = b.get();
                        System.out.println(myArg);
                    }
                }

                i. Java generics have the restriction that they must be
                   instantiated with a user-defined class. Hence generics
                   may not be instantiated with primitive types, such as ints.
                   If you want to use a primitive type, use its wrapper type
                   instead, such as Integer.

              ii. Generic Methods: You can also declare generic methods within
                   either non-generic classes, or within a generic class that
                   has a different type. For example, a generic printing method
                   that I place inside Box.java might look as follows:

                   public <V> void print(V data[]) {
                       for (V val : data) {
                           System.out.println(val);
                       }
                   }

                   Notice that there is a <V> in front of the void and
                   after the public keywords. If I had instead used T, I would
                   not have been required to use the leading <T>.

                   When I invoke a generic method, I may or may not have to
                   prefix it with the type of the object I am passing in:

                   Box b;
                   b.print(args);  // always works
                   b.print(args) // usually works

                   If you do not prefix the method call with the type of the
                   object you are passing as an argument, then the java compiler
                   will attempt to use type inference to determine the type
                   of the parameter. Usually this will be successful. If the
                   java compiler cannot determine the type and gives you an
                   error message, then you will have to explicitly prefix the
                   method with the type of the object you are passing to it.

              iii. Bounded Type Parameters: Sometimes you will want to 
                   guarantee that a type implements a certain interface. For
                   example, a sort method wants to have certain comparator
                   methods defined, such as equals and lessThan. You can
                   require a type to implement either an interface or class
                   using the keyword extends:
    
                   public <V extends Comparator> void sort(V data[]){...}

        2. C++ provides templates

	   a. Disadvantages

	       i. the code is very complicated to write, to debug, and to
	          understand

	      ii. a copy of the code must be created for each different type

	   b. Advantages: Supports both generic data structures and generic
	      functions
	          
	3. C provides void *'s

	   a. The programmer can create generic data types by declaring values
	      to be of type "void *"      
	      
	   b. The programmer can pass in type-specific functions (e.g.,
	      comparator functions) that take void *'s as parameters and
	      that downcast the void *'s to the appropriate type before
	      manipulating the data

	   c. Advantages

	       i. One copy of the code works with multiple objects
	       
	       ii. The approach supports both generic data structures and
	           generic algorithms--Note that this approach cannot be used
		   to support generic algorithms in Java since functions cannot
		   be passed as pointers

	   d. Disadvantages

	       i. Downcasting can be dangerous, even more so than in Java,
	          since run-time type checks are not performed in C

	      ii. The code often has a cluttered appearance (note how your
	          jval code looks)
    
    3. Both Java and C++ provide a set of generic data structures. 

        a. C++ provides the standard template library (STL), which includes
	   both templates for common data structures, such as lists and
	   hash tables, as well as common algorithms, such as quicksort.

        b. Java provides a set of standard data structures, such as lists
	   and hash tables, in its java.util package.