An Introduction to Generics in Java – Part 1

Posted: April 14, 2013 in Generics, Java
Tags: , ,

Generics play an important role in the development of Java applications. They provide stronger type checks at compile time, reduce the amount of typecasting needed and help to develop algorithms/procedures that can be reused for multiple types. In this series I will try to write a short tutorial about them.

A typical Generics Use Case – Implementing a Sorting Algorithm

Rather than learning generics by definition, let’s try learning it from one simple use case – implementing Insertion Sort.

We all know how Insertion Sort works. We are given an array of elements. Then the algorithm removes one element from the array, repeat over the existing ones, find the appropriate place for the removed element, and then inserts it there.

Let’s consider a simple implementation of this program in Java –

public class InsertionSort {
  private int[] elements;

  public int[] getElements() {
      return elements;
  }

  public void setElements(int[] elements) {
    this.elements = elements;
  }

  public void sort() {
    if (this.elements == null || this.elements.length <= 1) {
      return;
    }

    int i;
    for (i = 1; i <= elements.length - 1; i++) {
      int valueToInsert = elements[i];
      int holePos = i;

      while (holePos > 0 && valueToInsert < elements[holePos - 1]) {
        elements[holePos] = elements[holePos - 1];
        holePos--;
      }

      elements[holePos] = valueToInsert;
    }
  }
}

The above class implements a simple insertion sort algorithm. To sort an array of elements, we can use this class in the following way –

public class Main {
  public static void main(String[] args) {
    int[] elements = {3, 7, 4, 9, 5, 2, 6, 1};
    InsertionSort insertionSort = new InsertionSort();
    insertionSort.setElements(elements);
    insertionSort.sort();
    elements = insertionSort.getElements();

    // print the sorted array
    System.out.println();
    for (int i = 0; i < elements.length; i++) {
      System.out.print(elements[i]);

      if (i + 1 != elements.length) {
        System.out.print(", ");
      }
    }
    System.out.println();
  }
}

Now what if we want to sort array of floats, or doubles? Or, array of some custom objects? Can we use the above implementation for those cases?

No. Period.

The above algorithm only sorts simple integers. It cannot sort doubles, or floats, because in Java you cannot pass array of doubles to a method which expects an array of integer. So, if you want to sort doubles or floats, you’ll have to write another implementation which will then sort an array of doubles.

This approach has some serious drawbacks. For one, you’ll now have to maintain two different sorting algorithms, just to ensure that they operate on different types, even though the algorithm to sort them is exactly the same. From the principles of good Object Oriented Design, we know that this is bad, really really bad.

So, how can we change this implementation so that it works for doubles, or better yet, works for any types which can be compared with each other? Let’s pause for a moment here and try to think it by ourselves.

Really, don’t go read the next section. Thinking about why we need, or should use a language feature will help us to truly master it and use it effectively in our application.

Generics to the Rescue!

Ok, so you thought it through, right? If this is the case, then carry on!

What we will do is that we will somehow parameterize the above class so that it will now take types as its argument. This way, we will pass a new type as its argument whenever we will create a new instance of this class, which will then enable us to use the above algorithm for that type!

This is exactly what generics do! They enable types to be parameters when defining classes, interfaces and methods! From the official Oracle Java tutorial page

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types.

Let’s see the implementation first, then we will slowly understand how to write generics in the code and what syntax to use –

public class InsertionSort<E extends Comparable<E>> {
  private E[] elements;

  public E[] getElements() {
    return elements;
  }

  public void setElements(E[] elements) {
    this.elements = elements;
  }

  public void sort() {
    if (this.elements == null || this.elements.length <= 1) {
      return;
    }

    for (int i = 1; i <= elements.length - 1; i++) {
      E valueToInsert = elements[i];
      int holePos = i;

      while (holePos > 0 && valueToInsert.compareTo(elements[holePos - 1]) < 0) {
        elements[holePos] = elements[holePos - 1];
        holePos--;
      }

      elements[holePos] = valueToInsert;
    }
  }
}

Now, let’s dissect the above implementation to understand the syntax of generics –

  1. Take a look at the first line, the class declaration. Notice the extra <E extends Comparable<E>> part which is new? This is the part which adds generic behavior to the previous implementation. Here, E is the type parameter. We will go into the syntax details in the next section.
  2. Take a look at the elements declaration. After you declare a type parameter in the class declaration, you can then use it to declare a reference of that type. In our case, it’s an array.
  3. Similarly, our setter and getter now takes and returns array of type E, respectively.
  4. Take a look at the condition check inside the while loop, in the sort method. It has been changed to use the implementation of the Comparable interface. Keep a note of this because it will be explained in a later post.

and voila! With the above changes we can now use this class for lots of different types, provided that they all are of non-primitive types (yes, Generics don’t support primitive types) and implement the Comparable interface (this is to make sure that the elements can be compared with each other).

A sample use of the above class is as follows –

public class Main {
  public static void main(String[] args) {
    /**
     * Use the object wrapper for integer, because
     * generics don't support native types. The
     * following line will work due to Autoboxing.
     */
    Integer[] arr = {3, 7, 4, 9, 5, 2, 6, 1};

    /**
     * Check out the following line. It demonstrates
     * the way to pass type arguments to the
     * type parameters.
     */
    InsertionSort<Integer> sort = new InsertionSort<Integer>();
    sort.setElements(arr);
    sort.sort();
    arr = sort.getElements();

    System.out.println();
    for (int i = 0; i < arr.length; i++) {
      System.out.print(arr[i]);

      if (i + 1 != arr.length) {
        System.out.print(", ");
      }
    }
    System.out.println();

    /**
     * Let's use the implementation for
     * doubles!
     */
    Double[] anotherArray = {3.5, 7.2, 4.4, 9.45, 5.01, 2.50, 6.9, 1.11};
    InsertionSort<Double> anotherSort = new InsertionSort<Double>();
    anotherSort.setElements(anotherArray);
    anotherSort.sort();
    anotherArray = anotherSort.getElements();

    System.out.println();
    for (int i = 0; i < anotherArray.length; i++) {
      System.out.print(anotherArray[i]);

      if (i + 1 != anotherArray.length) {
        System.out.print(", ");
      }
    }
    System.out.println();
  }
}

Cool, eh 😀 ?

Generics Declaration Syntax

To declare a class as a generic one, we use the following format –

class name<T1, T2, ..., Tn> { /* ... */ }

The portion delimited by angle brackets in the type parameter section where you define the type parameters (they are also called type variables sometimes). The above declaration specified n type parameters – T1, T2, ….., Tn, and is called a generic type declaration. A type variable can be anything – any class type, interface, array or anything else, provided that it’s a non-primitive type.

After you declare a class in the above way and specify a type parameter, you can then use it like any other type inside that class, subject to the following conditions –

  1. You cannot create an instance of a type parameter.
  2. You cannot declare any static field whose type matches a type parameter.
  3. You cannot typecast or use instanceof operator with type parameters.
  4. You cannot catch or throw objects of parameterized types.

I know that the example I’ve provided looks more complex than the simple declaration syntax above. Keep your patience, I will certainly explain it in a future post (if I don’t, you are given the permission to poke me with a stick).

Let’s see another simple declaration of a generic class –

public class MyPrettySimpleGenericClass<E> {
  private E item;

  public void setItem(E item) {
    this.item = item;
  }

  public E getItem() {
    return item;
  }

  public void printItem() {
    System.out.println(item.toString());
  }
}

and a simple use case –

public class Main {
  public static void main(String[] args) {
    MyPrettySimpleGenericClass<Integer> item = new
        MyPrettySimpleGenericClass<Integer>();
    item.setItem(20);
    item.printItem();               // prints 20 to the console

    MyPrettySimpleGenericClass<Double> anotherItem = new
        MyPrettySimpleGenericClass<Double>();
    anotherItem.setItem(39.60);
    anotherItem.printItem();        // prints 39.60 to the console
  }
}

So now we’ve seen a simple use case of Generics. Thankfully that wasn’t too complex to understand.

That’s it for today, folks. Hopefully I will write the next post of this series pretty soon. If anything in the current post seems difficult to understand, or if you find any errors, please feel free to comment in!

Advertisements
Comments
  1. rohitjain1319 says:

    Your very first 2nd statement is wrong. “They provide stronger type checks at runtime, “. No they provide stronger type checks at compile time. Generics can’t provide type checks at runtime, due to type erasure.

  2. Flint says:

    Good post. Really useful.

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s