Generics in Java

In this topic, we will learn what is generics in Java and its example to understand in a simple manner. Generics means parameterized type that accepts all kinds of data types such as Integer, Double, String, etc., and user-defined types to assign as a parameter to the methods, classes and interfaces. Using Generic we will create classes that work different data types. The class, method or interface that works on parameterized types is known as a generic entity.

Why use Generics in Java?

1. Type Safety

Generics in Java provide us with type safety. As shown below example generics allowed us to check type mismatch during the compile-time instead of run time. This is a nice check for such errors during compile of the program. This allows for fixed code while compilation time.
Before Java Generics:

//Compiler doesn't know the type of the ArrayList in this code
//so it will allow both the additions and the program will
//throw an error while runtime.
ArrayList al = new ArrayList();
al.add(1001);
al.add("hi");

After Java Generics:

//Compiler knows the type of the ArrayList in the code
//so it will throw a compile-time error if you try to add any other type of data
//to this list which is not an integer
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1001);
al.add("hi"); //it will throw compilation error

2. No type cast required

With the help of Generics, there is no need to use type casting because when use Generics in our code compile already knows types of data returned from Collection such as ArrayList, Hashmap etc.

ArrayList<String> al = new ArrayList<String>();
al.add("hi");
al.add("bye");
//Before generics, we had to use typecasting in our code 
//like this: String s = (String)al.get(1);
String s = al.get(1); // there is no need to use typecasting required

3. Code Reusability

With the use of Generics, we are allowed to create a single class or method for multiple data types. We can an object of the ArrayList to accept multiple data types as shown. Rather than we can create an object of the ArrayList for a single data type. We can create an object of ArrayList for the desired data types. 

//List of Strings data
ArrayList<String> al = new ArrayList<String>();
//List of whole numbers data
ArrayList<Integer> al = new ArrayList<Integer>();
//List of float numbers data
ArrayList<Float> al = new ArrayList<Float>();
//List of characters data
ArrayList<Character> al = new ArrayList<Character>();
...etc

Types of Generics in Java

1. Generic class

A class is declared with the Type parameter(T) known as a Generic class. In the below example, we have a Test class as a declared Generic class and we can use this in any other class by passing desired data types such as Integer, String, Float, Double etc. We cannot pass primitive data types such as int, float, double, etc. as a parameter(T).

//Generic Class
class Test<T> {
//T is a type, which can accept any data type such as
//Integer, Double, String etc.
T obj;
Test(T obj) { 
this.obj = obj; 
}
public T getTheObject() { return this.obj; }
}
public class MainClass {
public static void main(String[] args)
{
Test<Integer> obj1 = new Test<Integer>(1001);
System.out.println(obj1.getTheObject() );
Test<Float> obj2 = new Test<Float>(20.55f);
System.out.println(obj2.getTheObject() );
Test<String> obj3 = new Test<String>("SpringJava");
System.out.println(obj3.getTheObject() );
Test<Character> obj4 = new Test<Character>('S');
System.out.println(obj4.getTheObject() );
  }
}

Output

1001
20.55
SpringJava
S

2. Generic Method

A method can accept different data types as a parameter known as a Generic Method.

public class Test{
//generic method
public static <T> void printData(T data) {
System.out.println("Data: "+ data);
}
public static void main( String args[] ) {
System.out.println("Calling a generic method with Integer");
printData(1001);
System.out.println("Calling a generic method with String");
printData("SpringJava");
System.out.println("Calling a generic method with Float");
printData(20.55f);
  }
}  

Output

Calling a generic method with Integer 
Data: 1001
Calling a generic method with String
Data: SpringJava
Calling a generic method with Float 
Data: 20.55

Type Parameters in Generics

We have seen that the Type parameter T is used in the above examples. We can also use different parameter types during working with Generics. These are the following below here:
T – Type
E – Element
K – Key
V – Value
N – Number

Example of Generics K and V parameters

The HashMap class is declared with K and V parameters. Here the K in generic is the type of the Key and the V in generic is the type of the Value. In the below example, HashMap accepts Integer key values and String values.

import java.util.HashMap;
import java.util.Map;
public class Test{
public static void main(String args[]){
HashMap<Integer,String> hashMap=new HashMap<>();
hashMap.put(1001,"Peter");
hashMap.put(1002,"Robert");
hashMap.put(1003,"John");
System.out.println("HashMap elements: ");
for(Map.Entry entry1 : hashMap.entrySet()){
System.out.print("Key: "+ entry1.getKey() + " & Value: ");
System.out.println(entry1.getValue());
    }
  }
} 

Output

HashMap elements: 
Key: 1001 & Value: Peter
Key: 1002 & Value: Robert
Key: 1003 & Value: John

Wildcard in Java Generics

Rather than defining specific types in generic such as Integer, String, Character etc., we can use the wildcard(?). There are three types of wildcards we can use in generics. These are the following below here:
   • Upper bound wildcards<? extends className>
   • Lower bound wildcards <? super className>
   • Non-bounded wildcards <?>

Upper bound wildcards

A<? extends B>

→ The  A class can accept the parameter type of the subclass of the B. For example, like this ArrayList declared as shown below can accept a parameter type of subclass(Integer, Double etc.) of Number class.

ArrayList<? extends Number>

Example of Upper bound wildcards

import java.util.ArrayList;
public class Test{
private static Double addList(ArrayList<? extends Number> numList) {
double sum=0.0; 
for(Number number:numList){
sum += number.doubleValue();
}
return sum;
}
public static void main(String[] args) {
ArrayList<Float> al1=new ArrayList<Float>();
al1.add(1.5f);
al1.add(2.0f);
al1.add(2.5f);
System.out.println("Sum of list elements: "+addList(al1));
ArrayList<Integer> al2=new ArrayList<Integer>();
al2.add(50);
al2.add(100);
al2.add(200);
System.out.println("Sum of list elements: "+addList(al2));
  }
}

Output

Sum of list elements: 6.0
Sum of list elements: 350.0

Lower bound wildcards

A<? super B>

→ The  A class can accept the parameter type of the B  or superclass of B. For example, like this ArrayList declared as shown below can accept a parameter type of Integer or superclass of Integer(Number).

ArrayList<? super Integer>

Example of Lower bound wildcards

import java.util.ArrayList;
public class Test{
private static void addList(ArrayList<? super Integer> numList) {
System.out.println(numList);
}
public static void main(String[] args) {
ArrayList<Integer> al1=new ArrayList<Integer>();
al1.add(50);
al1.add(100);
al1.add(200);
addList(al1);
ArrayList<Number> al2=new ArrayList<Number>();
al2.add(50);
al2.add(100);
al2.add(200);
addList(al2);
  }
}

Output

[50, 100, 200]
[50, 100, 200]

Non-bounded wildcards

A<?>

→ The A class accepts any parameter type.

Example of Non-bounded wildcards

import java.util.ArrayList;
public class Test{
private static void addList(ArrayList<?> list) {
System.out.println(list);
}
public static void main(String[] args) {
ArrayList<Integer> al1=new ArrayList<Integer>();
al1.add(50);
al1.add(100);
al1.add(200);
addList(al1);
ArrayList<String> al2=new ArrayList<String>();
al2.add("Hello");
al2.add("Generics");
al2.add("Wildcards");
addList(al2);
  }
}

Output

[50, 100, 200]
[Hello, Generics, Wildcards]

Conclusion

In this topic, we learnt about Generics in Java, why we use generics, types of generics and various types of wildcards in generics.

Leave a Comment