Generics allows you to define classes that can work with any defined type very similar to the use of templates in C++.
This provide type safety of the collection i.e. When you use collections like the vector and the stack you do not have to do any typecasting to get the correct value. This ensures that you cannot make silly errors with getting items from a collection.
Without generics we had to use typecasting to get the correct data out of structures. The other problem is we are going to have problems if try to add the wrong data type. Generics ensure that we use the right data type and that we do not have any typecasting to do.
If we use the wrong data type then the program will not compile.
Here is a simple example taken from the existing Collections tutorial:
// Removes 4-letter words from c. Elements must be strings
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); )
if (((String) i.next()).length() == 4)
i.remove();
}
Here is the same example modified to use generics:
// Removes the 4-letter words from c
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}
Creating generic classes
We can make our own generic classes. To do this we are going to make our own Stack class it is going to be a very simple Stack. If you don’t know, the Stack is a structure that has restricted access. You can only access the top item of the Stack. The stack provides a first-in, last-out order meaning you can only access the top item of the stack. Our stack will use a Vector as the underlying structure so it will be able to resize. Note that we cannot use an array as the underlying structure?
Why? This is because generic classes are compiled at runtime and the parameters are replaced with the appropriate type. However, arrays are initalized with a type at declaration and thus they cannot be generic. So our underlying structure will be a vector.
Creating a stack is really easy. All we need is a member variable that keeps track of the index of the top item. We store all the items in a private vector but only provide a method that can access the top item of the stack. The use of the private keyword in front of the Vector is what makes this structure possible. If we didn’t include it then we would just be creating a wrapper of the Vector and what is the purpose of that?
Creating the stack
To define that our stack is generic we use angle brackets after the class name and in those brackets we place the names of different parameters. These parameters will define what type of data the stack holds.
So the class skeleton looks like this:
Code:
public class Stack<E> {
}
Now what instance members do we need? We need a vector, and a counter for the index of the top. That is all. The vector will be of type E indicating that it holds a user defined data type and the index variable will be an integer set to -1.
So add these just after the class declaration.
Code:
private Vector<E> stack; // holds the contents of the stack
private int top; // the index of the top item in the stack. -1 indicates empty.
Now to declare the constructor.
Code:
public Stack() {
this.stack = new Vector<E>();
this.top = -1;
}
We create a new Vector that is empty, this is going to hold the stack. We will initalize the top variable to -1 which indicates that the stack is empty.
Now the next two methods we will define are push and pop. The push method adds an item to the top of the stack and the pop method removes an item from the stack and returns it. The push method:
Code:
public void push(E obj) {
stack.add(obj);
top++;
}
We use the add method in the vector to add the parameter to the stack. The parameter is of type E which will be replaced with the type defined by the programmer when it is used. We increase the top variable by 1 so we have the index of the top item stored.
The pop method is very similar to this, it returns type E and returns the item at the top of the stack. However we have to remove this item from the stack so we need to store it in a temporary variable.
We can’t just do
Code:
return stack.get(top);
Why? We have to do two other things, decrease the top variable by 1 so it points to the new top of the stack. We also have to remove the item from the Vector. If we don’t we will run into problems if we add a new item later. We will see this item again which is not what we want. The problem with this I illustrated in my tutorial about Vectors. We have to copy the vector contents into a new array every time we erase the end item. This can take a really long time for big stacks. We will only remove an item from the Stack if it is not empty. The pop method returns null if the stack is empty.
So the pop method looks like this:
Code:
public E pop() {
if (top == -1) {
return null; // stack is empty
}
E temp = stack.get(top);
stack.remove(top);
top–;
return temp;
}
This covers everything the Stack needs to do but it might be useful to define two methods that check if the stack is empty and how many items are in the stack. The stack is empty if top = -1. So the empty method looks like this:
Code:
public boolean isEmpty(){
return top == -1;
}
The number of items in the stack then is top+1. If the stack is empty (top = -1) and the number of items is 0.
Code:
public int size() {
return top+1;
}
So the full class looks like this:
Code:
import java.util.Vector;
public class Stack<E> {
private Vector<E> stack;
private int top = -1; // index of the top item of the stack
public Stack() {
stack = new Vector<E>();
}
public void push(E obj) {
stack.add(obj);
top++;
}
public E pop() {
if (top == -1) {
return null; // stack is empty
}
E temp = stack.get(top);
stack.remove(top);
top–;
return temp;
}
public boolean isEmpty(){
return top == -1;
}
public int size() {
return top+1;
}
}
Now to test this class you can use this code:
Code:
public class Generics {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Stack<Integer> st = new Stack<Integer>();
st.push(500);
st.push(400);
st.push(300);
st.push(900);
if (st.size() == 0) {
System.out.println(“Stack is empty.”);
} else {
System.out.println(“Stack contains ” + st.size() + ” items.”);
}
while (!st.isEmpty()) {
System.out.println(st.pop());
}
if (st.size() == 0) {
System.out.println(“Stack is empty.”);
} else {
System.out.println(“Stack contains ” + st.size() + ” items.”);
}
st.push(700);
System.out.println(“Size: ” + st.size());
System.out.println(st.pop());
}
}
Output of the Generics is
Stack contains 4 items.
Notice how in the angle brackets I have included Integer? This means that the stack can only hold integer objects. Next we add 4 items to the stack. If you run the code you will notice that the items are outputted in reverse order. This is how the stack works. Now notice how the size method works. If displays how many items are in the stack.
Output of the Generics is
Stack contains 4 items.
900We can note that st.size() == 0 is the same as isEmpty.
300
400
500
Stack is empty.
Size: 1
700
!Enjoy!
900
300
400
500
Stack is empty.
Size: 1
700