Generics are a way to provide Type parameters to your Classes and Methods (Type Parameters can be Classes or Interfaces) so that you can create a generic code that to which you can provide different Class depending upon the need. Generic can be better understood by looking at examples.

Generics was introduced in Java v1.5 . Java always had a Object Class that can be used to do what we can do today with Generics. But certainly there were disadvantages and problems of using that approach.

Let's consider an example that does not use Generics so that we can understand why there was a need to Generics.

public class NoGenerics {

    Object ob;

    NoGenerics(Object ob) {
        this.ob = ob;
    }

    public void showValue() {
        System.out.println(ob);
    }

    public Object getValue() {
        return ob;
    }

    public void showType() {
        System.out.println(ob.getClass().getName());
    }

}

We have created a class NoGenerics which has a variable of type Object since we wanted this class to be a Generalised class which can be initiated with any Object Type. We have created a Main class to see this class into working.

public class Main {

    public static void main(String args[]) {
        NoGenerics intObj = new NoGenerics(88);
        intObj.showValue();
        intObj.showType();
        int intValue = (Integer) intObj.getValue();

        NoGenerics stringObj = new NoGenerics("5Balloons");
        stringObj.showValue();
        stringObj.showType();
        String stringValue = (String) stringObj.getValue();
    }

}

There is no problem with compiling or running this code, But if you can look closely there are certain problems which even though don't cause errors now, But will certainly cause issues in Big Project. First there is no type checking on the Value we are passing to the NoGeneric Class. It accepts whatever we are sending it, There needs to be Type Casting done while assignment, since compiler does not know in advance what kind of Object is stored in our NoGeneric Class. All these problems are solved when Generics was introduced in Java. Let's fix the Class by using Generics.

public class GenericsExample<T> {

    private T t;

    GenericsExample(T t) {
        this.t = t;
    }

    public void showValue() {
        System.out.println(t);
    }

    public T getValue() {
        return t;
    }

    public void showType() {
        System.out.println(t.getClass().getName());
    }

}

We have created a new Class GenericsExample that uses Generics. Notice we have introduced a new parameter t which is an Instance of Type. Thus when creating an object of Class GenericsExample you need to specify the actual Type parameter. Let's see a Main Class where we bring this GenericsExample class into action.

public class Main {

    public static void main(String args[]) {
        GenericsExample<Integer> intObj = new GenericsExample<>(88);
        intObj.showValue();
        intObj.showType();
        int intValue = intObj.getValue();

        GenericsExample<String> stringObj = new GenericsExample<>("5Balloons");
        stringObj.showValue();
        stringObj.showType();
        String stringValue = stringObj.getValue();
    }

}

As compared to the NoGenerics Class, A Class that uses Generics provides some obvious benefits.

Advantages of using Generics

1. Stronger type checks at compile time. (If you instantiate a Class that uses Generics with Integer and somewhere in your code you try to instantiate or insert a String value into this, java compiler will give an Error)
2. Elimination of Casts. (Since the compiler knows the Type of objects being added into a Class there is no need to casting)
3. Enables us to write generic code and algorithms. (By using Generics we can create code that can be used by Classes/Collections of different types)