Java Generics Tutorial with Examples

Generics were added by JDK 5. Most of the algorithms are logically same irrespective of the type of data they are being applied to. By using generics you can define an algorithm once, and you can apply it on any kind of datatype without any additional effort.

What are Java generics?

Generics allows a type or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting.

The goal of implementing Generics is finding bugs in compile-time, other than in run-time. Finding bugs in compile-time can save time for debugging java program, because compile-time bugs are much easier to find and fix. Generic types only exist in compile-time. This fact is the most important thing to remember for learning Java Generics.

Java Generics Example

Generics was added in Java 5 to provide compile-time type checking and removing risk of ClassCastException that was common while working with collection classes. Let's see below basic example to understand benefits of generics.
List list = new ArrayList();
list.add(new Integer(2));
list.add("a String");//OK
for(Object obj : list){
        Integer intval=(Integer) obj; //type casting leading to ClassCastException at runtime
}


Above code compiles fine but throws ClassCastException at runtime because we are trying to cast Object in the list to Integer whereas one of the element is of type String.

Very often you only use a single type with a collection. For instance, you only keep String's or something else in the collection, and not mixed types like I did in the example above.

With Java's Generics features you can set the type of the collection to limit what kind of objects can be inserted into the collection. Additionally, you don't have to cast the values you obtain from the collection. Here is an example using Java's Generic's features:
List<Integer> intvals = new ArrayList<Integer>();
strings.add(new Integer(2));
String intval = intvals.get(0);


Java 5 also got a new for-loop (also referred to as "for-each") which works well with generified collections. Here is an example:
List<Integer> intvals = new ArrayList<Integer>();
strings.add(new Integer(2));
for(Integer intval : intvals){
 System.out.println(intval);
}


This for-each loop iterates through all Integer instances kept in the intvals list. For each iteration, the next Integer instance is assigned to the intval variable. This for-loop is shorter than original while-loop where you would iterate the collections Iterator and call Iterator.next() to obtain the next instance.

How to write Java Classes and Interfaces Using Generics

We can define our own classes and interfaces with generics type. A generic type is a class or interface that is parameterized over types. We use angle brackets (<>) to specify the type parameter.
 
public class GenericsClass<T> { 
    private T t;

    public void setVal(T t1){
        this.t=t1;
    }
 
    public T getVal(){
        return this.t;
    }
      
    public static void main(String args[]){
        GenericsClass<String> typeStr = new GenericsClass<String>();
        typeStr.set("A String Val"); 
        GenericsClass<String> typeInt = new GenericsClass<Integer>();
        typeInt.set(new Integer(2));          
    }
}


In the above example each instance of class GenericsClass have different types.
In similar way, we can use generics in interfaces. Comparable interface is a great example of Generics in interfaces and it’s written as:
package java.lang;
import java.util.*;
 
public interface Comparable<T> {
    public int compareTo(T o);
}


We can also have multiple type parameters as in Map interface. Again we can provide parameterized value to a parameterized type also, for example new HashMap<String, List<String>>(); is valid.

Generics Type Naming Convention

Naming convention helps us understanding code easily and having a naming convention is one of the best practices of java programming language. So generics also comes with it’s own naming conventions. Usually type parameter names are single, uppercase letters to make it easily distinguishable from java variables. The most commonly used type parameter names are:
  • E – Element (used extensively by the Java Collections Framework, for example ArrayList, Set etc.)
  • K – Key (Used in Map)
  • N – Number
  • T – Type
  • V – Value (Used in Map)
  • S,U,V etc. – 2nd, 3rd, 4th types

Generics Methods and Constructors in Java

Sometimes we don’t want whole class to be parameterized, in that case we can use generics type in methods also. Since constructor is a special kind of method, we can use generics type in constructors too.

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

The syntax for a generic method includes a type parameter, inside angle brackets, and appears before the method's return type. For static generic methods, the type parameter section must appear before the method's return type.

The Util class includes a generic method, compare, which compares two Pair objects:
public class Util {
    // Generic static method
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public class Pair<K, V> {

    private K key;
    private V value;

    // Generic constructor
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    // Generic methods
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}


The complete syntax for invoking this method would be:
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);


The type has been explicitly provided, as shown in bold. Generally, this can be left out and the compiler will infer the type that is needed:
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.

NEXT READ : Generics bounded types, wildcard and Type Erasure Tutorial with examples.
Java Generics Tutorial with Examples

Hope we are able to explain you basic details of Java Generics, if you have any questions or suggestions please write to us using contact us form.(Second Menu from top left).

Please share us on social media if you like the tutorial.