Generic Classes - top of Musings page

by Nick Duchon

Internal links:

 Introduction

A template type, also called a parameterized type, consists of a class name followed by a list of formal types inside angle brackets:
MyStack <X> // for example.

Formal types must be given actual types everywhere they are used at compile time. The compiler then makes sure that all references in the code have the correct types, thereby reducing the need for type casting when using the class in question. Usually, the need for casting is completely eliminated.

 The Problem

Let us see a few examples.

The Vector class is an excellent example.

// Using Vector without parameters
import java.util.Vector;

class TemplateTestOne {
   public static void main (String args []) {
      Vector v = new Vector ();
      v.add ("One");
      String o = (String) v.get(0);
      System.out.println ("o: " + o);
   } // end main
} // end class TemplateTestOne:

Compiler messages:
 ----jGRASP exec: javac -g F:\Nick\java\cm241\duchon\Templates\TemplateTestOne.java

Note: F:\Nick\java\cm241\duchon\Templates\TemplateTestOne.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

 ----jGRASP: operation complete.

Recompiling with -Xlint:
----jGRASP exec: javac -g -Xlint F:\Nick\java\cm241\duchon\Templates\TemplateTestOne.java

TemplateTestOne.java:6: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.Vector
      v.add ("One");
            ^
1 warning

 ----jGRASP: operation complete.

The compiler really wants to create a structure of some particular type, not just a generic Object type, so that it can check the inserted and extracted objects by type.

 Solution

Here's the solution using templates:

import java.util.Vector;

class TemplateTestTwo {
   public static void main (String args []) {
      Vector <String> v = new Vector <String> ();
      v.add ("One");
      String o = v.get(0); // NO need for casting here
      System.out.println ("o: " + o);
   } // end main
} // end class TemplateTestTwo

This code no longer has a compiler error!

 Creation - Stack

How to create your own template classes? What we have seen so far is how to use a template class. Here is an example of a linked list stack class with a parameter.

Note that the formal parameter appears all over the place, telling the compiler that all of these references are to the same type parameter. This example uses an inner class, and you can see how the parameter flows through the whole of the project. Leaving off any of the <E> references will result in unsafe reference errors from the compiler.

class MyTStack <E>
{
    int size = 0;
    MyTNode<E> top = null;
   
    class MyTNode <E> {
        E data = null;
        MyTNode<E> next = null;
    } // end inner class MyNode
   
    public void clear () {
        top = null;
    } // end clear
   
    public void push (E ob) {
        MyTNode <E> m = new MyTNode <E> ();
        m.data = ob;
        m.next = top;
        top = m;
    } // end push
   
    public E pop () {
        MyTNode <E> m = top;
        if (top == null) return null;
        top = m.next;
        return m.data;
    } // end method pop
   
    public String toString () {
        String st = "";
        MyTNode <E> current = top;
        while (current != null) {
            st += "\n" + current.data;
            current = current.next;
        } // end while
        return st;
    } //end toString method
} // end class MyStack

 Creation - Queue

Here's the modifications needed to make a generic queue.

Comments:

  1. Of course, you need to use generics in the declarations, rather than the name of the target class - lines 1, 3, 6, 17, 21, 28.
  2. The ACTUAL class used in the queue is only declared by the user of the MyQueue class - inside main in this case, line 37.
  3. Lines 38-40, where instances are added to MyQueue, are where the compiler checks for compatibility between the objects being inserted and the data type of the queue.
  4. This approach needs another class, Node (line 46) (called LLObjectNode in DJW pg 322), for the front and back links. Note that that class is also generic, and it is most convenient to make clear that the generic parameter that class uses is different (from the compiler's point of view) than the one used in the generic definition of the MyQueue class.
  5. The information about the generic class is passed from main (line 37) to MyQueue (line 1), on to Node on line 3.
  6. The determination of the queue size is best done using the length instance variable - lines 8 and 18.
  7. toString is VERY useful to tell if the queue is really working correctly. Lines 26, 52 and 61.
  8. Line 41 - this is REALLY simple because we have defined nice toString methods.

public class MyQueue < T > { // line 1
 Node < T > front, back; // line 3
 int length = 0;


 public void enqueue(T o){ // line 6
   ++length;
   if (length == 1){ // line 8
     front = back = new Node < T > (o);
   } else {
     back.next = new Node < T > (o);
     back = back.next;
   }
 }

 public T dequeue() { // line 17
   if (length <= 0) // line 18
     return null;
   --length;
   T toDequeue = front.data; // line 21
   front = front.next;
   return toDequeue;
 } // end method toDequeue - generic

 public String toString () { // line 26
   StringBuffer sb = new StringBuffer ();
   Node < T > curr = front; // line 28
   while (curr != null) {
     sb.append (curr + "\n");
     curr = curr.next;
   } // end while
   return sb.toString();
 } // end toString

 public static void main (String args []) {
   MyQueue < QueuedStudent > mq = new MyQueue < QueuedStudent > (); // line 37
   mq.enqueue (new QueuedStudent ("One")); // line 38
   mq.enqueue (new QueuedStudent ("Two")); // line 39
   mq.enqueue (new QueuedStudent ("Three")); // line 40
   System.out.println ("Queue: \n" + mq); // line 41
 } // end main
} // end class MyQueue - generic

class Node < S > { // line 46
 Node < S > next;
 S data;

 public Node (S d) { // line 50
   data = d;}
 public String toString () { // line 52
   return data.toString();}
} // end class Node

class QueuedStudent {
 String name;

 public QueuedStudent (String s) {
   name = s;}
 public String toString () { // line 61
   return name;}
} 

WildCards

<? extends B>

B or one of its child classes

Classes are: B, C, D

<? super B>

B or one of its parent classes

Classes are: B, A, Object