CSCI 162 - Lecture 7 - Generic Programming - Chapter 5

Instructor's Class Notes

     

  1. What is Generic Programming? - Generic programming is the practice of developing generic classes that can be used for any Java primitive data type or class.
  2.  

  3. Why Program Generically? - The Bag, Sequence (arrays and linked list) and other ADT classes written have all worked with one, specific data type. For example: IntBagArray can only hold integers. Generic programming provides a better approach than having 8 (the number of primitive data types) different classes, each of a different type.
  4.  

  5. Java's Object Type and Wrapper Classes:
    1. Object - Java has a data type called Object and a variable of that type can hold a reference to any kind of object.
    2. Widening Conversion - A widening conversion is done through an assignment in which there is a specific data type on the left-hand side and an object on the right-hand side. No cast is required.

      Example:
      String s = new String("Objection overruled!");
      Object obj;
      obj = s;

      Resulting in:
    3. Narrowing Conversion - A narrowing conversion is an assignment with an object on the right-side and a specific data typed variable on the left-side and always requires a cast.

      Example:
      String s = new String("Objection overruled!");
      Object obj;
      obj = s;
      s = new String("Make it so.");
      ...
      s = (String) obj;

      Resulting in:

     

  6. Wrapper Classes: - Almost everything in Java is an object with the exception of the 8 primitive data types. For those, there exist wrappper classes which are classes that provide a object oriented implementation for variables of that same type.

  7.  

  8. Object Methods & Generic Methods - Instead of developing almost identical classes and methods that only change in data type (e.g. IntBagArray vs DoubleBagArray you could use Object as the data type.

    For example:
    
    public static Object middle( Object [] data) {
        if (data.length == 0)
        { // The array has no elements, so return null.
            return null;
        }
        else
        { // Return an element near the center of the array.
            return data[data.length/2];
        }
    }            
                
    Why Not Just Do This? - Well, using objects instead of generics is not quite a useful as you have to make sure to cast your variables to and from objects in order for everything to work and some casts may not work at runtime causing errors you can't catch as you compile.

    Using Generic Method - A generic method is a method that is written with the types of the parameters not fully specified. Our middle method is written this way as a generic method:
    
    public static <T> T middle(T[] data) {
        if (data.length == 0)
        { // The array has no elements, so return null.
            return null;
        }
        else
        { // Return an element near the center of the array.
            return data[data.length/2]; 
        }
    }            
                
    Generic Type Parameter - <T> is a generic type parameter and can eventually be supplied by the programmer when calling the method and supplied a variable of any class type. The generic type parameter should be one character and should always be included with its angle brackets before the return type of the same letter in the method. This syntax indicates to the Java compiler that anywhere else in the method the generic type is replaced with that single letter.

    Convention Alert! - The designers of Java recommend using a capital T for data types and a capital E for elements when specifying a generic type parameter.

    Generic Type Restrictions - A generic type must be of a class type and cannot be one of Java's primitive data types.

    Review: - A generic method is written by putting a generic type parameter in angle brackets before the return type in the heading of the method.

    For example:
           static <T> T middle(T[] data)...
    The generic data type is often named with a single capital letter, such as T for “type.” The name T can then be used in the rest of the method implementation as if it were a real class name (with a few exceptions).

    A programmer can activate a generic method just like any other method. The compiler will look at the data types of the arguments and infer a correct type for each generic type parameter. The inferred types help catch type errors at compile time.

    For example:
            // i is an integer; ia is an Integer array; c is a Character
                
            i = middle(ia); // This is fine
            c = middle(ia); // Compile-time type error
  9.  

  10. Generic Classes - A generic method is a method that depends on an unspecified underlying data type. In a similar way, when an entire class depends on an underlying data type, the class can be implemented as a generic class.

    For Example:
    
    public class ArrayBag<E> implements Cloneable
    {
        // Invariant of the ArrayBag<E> generic class:
        //   1. The number of elements in the bag is in the instance variable manyItems.
        //   2. For an empty bag, we do not care what is stored in any of data;
        //      for a non-empty bag, the elements in the bag are stored in data[0]
        //      through data[manyItems-1], and we don't care what's in the rest of the data.
        //
        //
        private Object[ ] data; 
        private int manyItems;

    Note: The new bag stores an array of Objects rather than an array of int values. At run time, each Object will actually have type E.

    Important Considerations:
  11.  

  12. Generic Nodes - A generic node is like any other generic class that uses generic types, but it provides a node for a linked list that can be used for any data type linked list.