The Power Of Generics

Waking up in the morning, deciding what to drink, filling up your cup. Surprisingly! You are leveraging the power of generics.

Fill your cup with any drink, and Enjoy the power of generics —unsplash
“By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.”
Java Documentation

What’s The Problem?

If you ever wanted to implement data types like lists, stacks, or queues. In the stack or queue class, we define the data type of the items. So, we will have stack of integers, strings, or whatever.

But, what if we want to have stack of other data types(including user defined), like stack of apples, books, dishes, URLs, …etc.

First Try

Implement a stack class for apple data type. But, this won’t allow us to use other types, as books. We can only create stack of apples.

Even implementing a separate stack class for each data type is not a practical solution as you’ll have to copy and paste the same code over and over again which is tedious and error-prone.

Second Try

Why not to use stack of “Objects” type since all objects inherits from it by default. So, you are free to use whatever data type you want, provided that it’s not a primitive type.

But, whenever you get an item from the stack, you have no clue what’s the data type of this item; Is it an apple or an orange.

StackOfObjects stack = new StackOfObjects();
stack.push(new Apple());
stack.push(new Orange());
Apple a = (Apple) s.pop();    // Run-time error! casting won’t help

Third Try

What if we can pass the data type we want to use with the stack. So, if we want a stack of apples, or any other type, we just declare it. We’re flexible to create a stack of any type. No casting needed, no run-time errors. This is the power of generics.


With generics, we can have a list, a stack, or a queue of different types including user defined. It provides a way to re-use the same code with different data types. Before moving on, let’s examine a non-generic class.

A Simple Stack Class

We have a Stack class of items, each of type “Object”. Just as we did with our Second Try(see above).

public class Stack {
private Node first = null;

private class Node {
Object item;
Node next;

public void push(Object item) { /* … */ }
public Object pop() { /* … */ }

A Generic Stack Class

Now to create a generic class, It’s defined with the following format:

class name<T1, T2, …, Tn> { /* … */ }
T1, T2, …, Tn are the type parameters delimited by angle brackets (<>), and follows the class name.

To update the Stack class to use generics, we add the type parameter, and use it instead of “Object” type.

public class Stack<Item> {
private Node first = null;

private class Node {
Item item;
Node next;

public void push(Item item) { /* … */ }
public Item pop() { /* … */ }

Generic Arrays

Java doesn’t allow generic array creation. It allows to declare an array with an apple objects, but you can’t declare an array with generic type.

public class Stack<Item> {
private Item[] s;
private int N = 0;

public Stack(int capacity){
s = new Item[capacity]; // not allowed

The solution is to make an array of “Objects” type, and cast the array of objects to the generic type. It’s called “ugly cast”.

public Stack(int capacity){     
s = (Item[]) new Object[capacity];

Naming Conventions

Type parameter names are single, uppercase letters. The most commonly used type parameter names are: T for type, K for key, V for value, …etc.

public class Pair<K, V> { /* … */ }

Using Generic Class

Now, we can create a stack of apples, books, or whatever the data type is.

Stack<Apple> appleStack   = new Stack<Apple>();
Stack<Orange> orangeStack = new Stack<Orange>();
appleStack.push(new Apple());
orangeStack.push(new Orange());

Type Parameter

A type parameter can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.

Java doesn’t allow to have primitive types in generic types. So, use the primitive wrappers, like Integer.

Stack<Integer> stack = new Stack<Integer>();

Autoboxing and Unboxing

A process of automatically casting primitive data types into its equivalent wrapper type. If the conversion goes the other way, this is called unboxing.

Stack<Integer> s = new Stack<Integer>();
s.push(17); // equivalent to: s.push(new Integer(17));
int num = s.pop(); // equivalent to: s.pop().intValue();

Type Mismatch Errors

A Java compiler discovers type mismatch errors of generic types at compile-time instead of run-time. So, inserting an orange object in the stack of apples will result in a compile error.

Fixing compile-time errors is easier than fixing run-time errors, which can be difficult to find. So, welcome compile-errors, and avoid run-time errors.

Stack<Apple> stack = new Stack<Apple>();
stack.push(new Apple());
stack.push(new Orange()); // Compile-time error
Apple a = s.pop();

Why Use Generics?