DEV Community

Cover image for Generics In Java
yash sugandh
yash sugandh

Posted on

Generics In Java

What is the meaning of Generics?

One of the definitions from Java docs :

Generics allow you to abstract over types. The most common examples are container types, such as those in the Collections hierarchy.

In English:

Abstract over type means to group together similar and generalize them.

For Example: what do we do if we need to save firstName, lastName and email of multiple people do we create n number of variables to store and retrieve them manually OR
we create an Abstraction over it known as Class.

Great!, Now we know what abstract over type means, but I still don't get how is it beneficial to us???

In short: Generics provides us with compile time type-checking and saves the need of manual type-casting.

In depth: Let's find out

Let's take an example of how Collection's used to work before introduction of generics.

List numList = new ArrayList(); // 1
numList.add(new Integer(1)); // 2
Integer x = (Integer) numList.iterator().next(); // 3    
Enter fullscreen mode Exit fullscreen mode

Line 1 and Line 2 are simple, we created an ArrayList and added an Integer.

On Line 3 we have something that looks painful and will probably make you a little irritated Manual Type Conversion

Just imagine doing that every single time.

In the above case even though the developer knows that the data is Integer at compile time we still need to manually convert it to Integer.

This was the best case scenario where even though it looks bad it still works what if

List list = new ArrayList(); //1
list.add("abc"); //2
list.add(new Integer(5)); //3 

for(Object obj : list){
    Integer numbers=(Integer) obj; 
}
Enter fullscreen mode Exit fullscreen mode

Line 1 we created an ArrayList
Line 2 we add a String to list
Line 3 we add an Integer to the list

Wait what!!
We first added a String and now an Integer to the same list

Okay what's next
we iterate over List and try to manually type cast it to Integer

So, we added a String and an Integer, and now we are expecting both of them to be type-casted to an Integer

You know what's coming

ClassCastException 🎆 💥

We all know someone who is capable of doing this 😉

What were the problems we found till now:

  1. We need an abstraction over these so that we don't need to manually convert it every time
  2. We need a compile time check to save us from ClassCastException

This is what our savior Generics helps us with

How? Let's find out

List<String> listOfString = new ArrayList<>(); //1
listOfString.add("Generics"); //2
listOfString.add("are"); //3
listOfString.add("Awesome"); //4

for(String str : listOfString){ //5
     //no type casting needed, avoids ClassCastException
}
Enter fullscreen mode Exit fullscreen mode

Most of the lines in the above code looks just as before except the manual typecast is gone 😌 .

There is also something new on line 1
List<String> listOfString = new ArrayList<>();

<> is used in generics to help us define the type of data we are going to store in the list.

That's it that all we need to do for adding a compile time type safety.

What if someone tries to add an Integer to the above list?

If we try to add an Integer we get a compile time error.

So, can we use Generics only with Collections?

No, Collections is just one of the places where we can use Generics. Let's look at other places where we can use Generics.

Let's start small

Generic Interface

interface Print<T>{
  void display(T input);
}
Enter fullscreen mode Exit fullscreen mode

We can create generic interfaces which can be implemented by Classes with their respective data-type

class PrintInteger implements Print<Integer>{

  @Override
  public void display(Integer input) {
    System.out.println("Printing an int "+input);
  }
}
Enter fullscreen mode Exit fullscreen mode

We have implemented Print interface and specified the data-type as Integer.

Generics Class and Generic methods

Let's take a situation where we want to store firstName, lastName and an id of an employee but the data-type of id can be an int or long or String.

Code without Generics

class EmployeeWithIntegerId {
private String firstName;
private String lastName;
private int id;
}

class EmployeeWithLongId {
private String firstName;
private String lastName;
private long id;
}

class EmployeeWithStringId {
private String firstName;
private String lastName;
private String id;
}
Enter fullscreen mode Exit fullscreen mode

We can see that without using generics we have to create 3 different classes to cater the situation.

Now, let's use generics to get us out of this situation

Code with Generics

class Employee<T>{
  private String firstName;
  private String lastName;
  private T id;
}
Enter fullscreen mode Exit fullscreen mode

T in the above code represents the data-type of our id which will be decided by us while creating the instance of the class.

Is T mandatory to use?

No, T is just the most commonly used one, but it is not mandatory. We can use any alphabet we like in place of T.

For people who are interested in the best practices in naming convention

The most commonly used type parameter names are:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
Enter fullscreen mode Exit fullscreen mode

courtesy of java docs

Now, when we use generics we only needed to create a single class where the only thing that needs to be decided was the type of our variable id.

Okay, but how will the getters, setters and constructor work ??

Constructor

  public Employee(String firstName, String lastName, T id) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.id = id;
  }
Enter fullscreen mode Exit fullscreen mode

The constructor will look the same as before. It is one of the best and easy to understand example of generic methods.

Getters and Setters

  public T getId() {
    return id;
  }

  public void setId(T id) {
    this.id = id;
  }
Enter fullscreen mode Exit fullscreen mode

Okay, so we have defined the class, so how can we use it??

Employee<String> employee= new Employee<>("yash","sugandh","1234"); 
Enter fullscreen mode Exit fullscreen mode

In the above example we created an object of the class Employee and while creating it we used <> operator to define the data-type of T as String.

There is a restriction on data-type that we cannot use primitive data-types, so we cannot use int we have to Integer, Long instead of long and so on.

Now, we know about generics and how it helps us in Collections, Interface, Classes and methods.

But, there are still few questions left

  1. What are wildcards in generics ?
  2. What is the meaning of bounded and unbounded wildcard ?
  3. How does Java ensure backward compatibility from code with generics to code without generics?

Let's cover all these topics in the next post.

Please let me know if there are any questions in the comments below.

See you in the funny papers 🚀

Top comments (0)