Java Generics<Part-1>: A Basic Introduction

Welcome to the world of Java Generics, a feature introduced long back in Java 5, along with some others like, boxing/unboxing, enhanced for-loop, enum, varargs, etc. Though it is already a bit old feature, still many find it difficult to digest some of the tricky part of it. This is the first post among the series of posts, that I’ll probably write on this feature, considering both amateur and intermediate Java Developer.

What is Java Generics?

We all know that writing bug-free code on the very first attempt is like a nightmare. Even the most experienced programmers can often have bugs in their code. And these bugs can often increase the cost of the software we write. This increase in cost is directly proportional to the delay in discovery of those bugs. The earlier a bug is detected, the lower is the cost of fixing it.
Generics helps us to detect the bugs early at the compilation time, rather than letting it persist till runtime, by doing stronger type check at compile time. Yes, generics is all about compile time activity. It has no business at runtime.

How generic code differs from non-generic code?

Let’s consider a very simple example, where you create a List, add an element, and then fetch it back. We will see code with and without generics (as you would write in Java versions < 5)

1. Before Java 5:

List list = new ArrayList();
list.add(new Integer(5));
// list.get(0) returns an Object, so you need a cast to Integer
int val = (Integer)list.get(0);

As you see, before generics were introduced, you would have to cast the value fetched from the list to appropriate type, since the list is nothing but a list of Object. Well and good till now.
But consider the case, when you add a String into your list, and cast it to an Integer while fetching:

List list = new ArrayList();
list.add("abc");                  // Will compile fine
int val = (Integer)list.get(0);   // ClassCastException at runtime

Now, there is no way for the compiler to know what type you actually get from the list. And then this will blow at runtime, with a ClassCastException.

Generics avoid this issue, by providing generic type List, which contains a type parameter as a part of it’s declaration – List<E>. The actual type argument is passed while instantiating the List, which replaces the type parameter E. The actual type argument specifies the type of element that will be stored in that list. We’ll discuss later on, what is the significance of E. So, let’s see how the same code looks like with generics:

2. Java 5 onwards:

List<Integer> list = new ArrayList<Integer>(); // Instantiate generic type List
list.add(5);  // Autoboxing from 5 to Integer reference
int val = list.get(0);  // No type cast needed now

Now, this code looks pretty more clear, both to you and the compiler. Passing the type argument Integer while instantiating the List, specifies that the list will contain element of type Integer. Since the compiler knows this, any attempt to add a type incompatible with Integer will result in a compile time error. And for that reason only, you don’t need to add a cast while fetching an element from the list. Note that, the auto-boxing feature introduced in Java 5 allows us to write – add(5); instead of add(new Integer(5));. Now, modify the code, and try adding a String to the list, or assigning the result of list.get(0) to a String, and see what happens.

List<Integer> list = new ArrayList<Integer>(); 
list.add("abc");  // This won't compile itself.
int val = list.get(0);

So, it seems interesting, how generics enforces type check at compile time, to avoid potential ClassCastException at runtime.

If you remember, I said that generics has no business at runtime. Even though there is a considerable difference in the code you write with or without generics, the generated bytecode are same for both the codes. Surprised? Well, this is the tricky part to understand. The compiler removes all the generic type information from the code as a process of type erasure. We’ll discuss this in details later on. So, there is no difference between both the above shown codes as far as runtime performance is concerned. Of course, compiler adds appropriate cast, to make the code still applicable.

A better example:

Suppose we want to create a list of 3 integers, and then iterate over the list, and print the contents. I’ll show you how the code would look both with and without generics. As an exercise, I would not explain the already explained concept, and let you figure out what is happening.

1. Before Java 5:

List intList = new ArrayList(Arrays.asList(
                   new Integer[] {
                            new Integer(1),
                            new Integer(2),
                            new Integer(3)

for (Iterator iter = intList.iterator(); iter.hasNext();) {
    int element = (Integer);

Since, there was no varargs, or autoboxing before Java 5, that is why you have to explicitly create new Integer[] array to pass to Arrays#asList(Object[]) method. Also, you had to create integer objects like – new Integer(1);. This is not the case from Java 5. Let’s see the code from Java 5 onwards:

2. Java 5 onwards:

List<Integer> genericList = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
for (int element: genericList) {

The features used in the above code are:

  • Generics
  • Varargs
  • Autoboxing
  • Enhanced for-loop

I’ll wrap up this post here only. In the next post we’ll see how to write a generic type yourself.


One Response to Java Generics<Part-1>: A Basic Introduction

  1. Pingback: Java Generics<Part-2>: Getting your hands dirty (Really?) | R.J. Code Blog

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: