Generic ClassesNicholas Duchon: Jul 3, 2019 |
Key Concepts: |
Language Spec |
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.
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.
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!
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
Here's the modifications needed to make a generic queue.
Comments:
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;}
}
<? 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 |
![]() |
![]() |
The following example contrasts compiler errors, compiler
warnings and run time errors using a HashMap example.
import javax.swing.JButton;
import java.util.HashMap;
public class GenericTest {
HashMap <String, JButton> a = new HashMap <> ();
HashMap <String, String> b = new HashMap <> ();
// c does NOT use generics
// this code will generate 7 compiler warnings - NO ERRORS!
// on lines: 10, 10, 29, 30, 32, 33 and 34
HashMap c = new HashMap (); // 2 warnings
GenericTest (String s) {
a.put (s + "a", new JButton ("one"));
a.put (s + "b", new JButton ("two"));
// the following is a compiler error
// b.put (s + "c", new JButton ("three"));
b.put (s + "d", "four");
b.put (s + "e", "five");
// NOTE: the compiler understands that values in a are JButton's
// so the following is ok
// >>>> there is NO casting here!
a.get (s+"a").setBackground (java.awt.Color.BLUE);
// NOTE: the compiler flags the following as an error since
// the values in b are String's, NOT JButton's
// b.get (s+"e").setBackground (java.awt.Color.RED);
c.put (s + "a", new JButton ("one")); // warning
c.put (s + "b", new JButton ("two")); // warning
// the following is a compiler error
c.put (s + "c", new JButton ("three")); // warning
c.put (s + "d", "four"); // warning
c.put (s + "e", "five"); // warning
// NOTE: the compiler accepts the PROMISE that the
// value retrieved from c is in fact a JButton
// which is true, so the following is ok
// >>>> casting is REQUIRED by the compiler here!
((JButton) c.get (s+"a")).setBackground (java.awt.Color.BLUE);
// NOTE: the compiler accepts the PROMISE that the
// value retrieved from c is in fact a JButton
// which is FALSE, so the following will result in a RUNTIME exception
// >>>> casting is REQUIRED by the compiler here!
((JButton) c.get (s+"e")).setBackground (java.awt.Color.RED);
} // end String constructor
public static void main (String [] args) {
new GenericTest ("test");
} // end main
} // end class GenericTest