Java Generics Tutorial | Using Generics With Collections Framework

In this first part of a series of Java Generics tutorials, we’ll look at basic usage of generics with collection framework. This is the most common use case of generics. In later parts of the tutorial, you’ll learn advanced topics like how to create your own generic classes in Java.

What is Generics

Generics is a powerful mechanism which results in cleaner code which is robust and easy to understand. Generics allows you to bind classes and objects to certain types. The most common use of generics is with collection classes like ArrayList, HashMap etc.

If you’ve learned Java programming after Java version 5 or 6 then you might already be using generics and don’t know about it. If you’ve written this type of code then you are already using generics,

ArrayList<String> userNames = new ArrayList<String>();

The <String> part is generics syntax. If you are not familiar with this syntax then we’ll see what it means and what advantages it provides.

If you are not familiar with collections framework in Java, its a set of Interfaces and Classes in the Java API. These classes provide you the ability to store multiple values like arrays but unlike arrays, the size of collections can change if more elements are added to them. Collection classes also provide you the ability to store key-value pairs.

Type Safe Collections

Using generics provides you type safety while using collections. Type safety in very simple terms is avoiding runtime exceptions by finding errors at compile time.

Let’s see an example of what the problem is and how generics solves it. Here is a simple piece of code that creates a List adds some elements to the list and then retrieves the elements from the list to get their total.

List numbers = new ArrayList();  //raw or untyped list
int total = 0;
for(int i = 0; i < numbers.size(); i++) {
    Integer number = (Integer)numbers.get(i);
    total += number.intValue();

When you run this code you’ll get a nice looking stack trace of a ClassCastException as output.


The second element in the List is a String so when our code tries to type cast it to Integer, then JVM throws this exception. Generics provides you the power to ensure this doesn’t happen. The compiler will flag such a problem to you instead of an exception at runtime. Here’s how the code will look when generics is included.

List<Integer> numbers = new ArrayList<Integer>();  //typed list
number.add("20");  //compile time error
int total = 0;
for(int i = 0; i < numbers.size(); i++) {
    Integer number = numbers.get(i); //no cast needed
    total += number.intValue();

This time the compiler knows that the numbers list can only contain elements of Integer type. So when you try to add a String to this list the compiler will report that error. As you might have noticed, you don’t need a type-cast when you get an element from the list at line no. 6. This is because again the compiler knows that elements in numbers List are of type Integer so the type-cast is automatically done by the compiler.

With an untyped List, you can add any type of elements to the list, and the compiler won’t complain about it. With generics the compiler would only let you add only objects of the type which you used while creating the list. Lets see a simple example,

List untypedList = new ArrayList();
//any object can be added to untypedList
untypedList.add("Hello World");
List<String> stringList = new ArrayList<String>();
//only String objects can be added to stringList
stringList.add("Hello World");
stringList.add(123);  //compile time error, cannot add Integer to String List

The above code demonstrates that with untyped lists, the programmer has to ensure he/she is adding the right type of objects to a List. If a wrong element gets added to the List, you can get a ClassCastException while retrieving elements from the List. But with generics, the compiler takes care of that. Like at line no. 8 we tried to add an Integer object to a List which takes only String objects. The compiler would disallow this making your life easier.

Using generics syntax makes your code more readable and avoids accidental addition of wrong objects into a collection. Like lets say you were writing a piece of code which took a List of Employee class objects and prepared two lists of employee names (String objects) and ids (Long objects). Here’s the code without generics,

class Employee {
    private String name;
    private Long id;
    public String getName() {
        return name;
    public Long getId() {
        return id;
    //name and id setters
public void manageEmployees(List employees) {
    List empNames = new ArrayList();  //raw list
    List empIds = new ArrayList();    //raw list
    for(int i = 0; i < employees.size(); i++) {
        Employee employee = (Employee)employees;
        empIds.add(employee.getName());  //did I do something wrong here?
    //do something with empNames and empIds lists

All is fine with this code except that I accidentally added employee names to employee ID list. So I’ll get an exception at runtime when I try to use the information stored in the empIds list as I’ll be expecting Long object but I’ll get a String object. If I were using generics, the compiler would have caught this. Lets see a generic version of this code,

public void manageEmployees(List<Employee> employees) {
    List<String> empNames = new ArrayList<String>();  //List of Strings
    List<Long> empIds = new ArrayList<Long>();  //List of Longs
    for(int i = 0; i < employees.size(); i++) {
        Employee employee = employees;
        empIds.add(employee.getName());  //compile time error
    //do something with these two lists

In this code the compiler would flag an error at line no. 7 that you cannot add a String object to a List which only takes Long objects. Such small things can save you hours of debugging.

Leave a Comment