Java Generics allows us to create a single class, interface, and method that can be used with different types of data (objects).
This helps us to reuse our code.
Note: Generics does not work with primitive types (int
, float
, char
, etc).
Java Generics Class
We can create a class that can be used with any type of data. Such a class is known as Generics Class.
Here's is how we can create a generics class in Java:
Example: Create a Generics Class
class Main {
public static void main(String[] args) {
// initialize generic class
// with Integer data
GenericsClass<Integer> intObj = new GenericsClass<>(5);
System.out.println("Generic Class returns: " + intObj.getData());
// initialize generic class
// with String data
GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
System.out.println("Generic Class returns: " + stringObj.getData());
}
}
// create a generics class
class GenericsClass<T> {
// variable of T type
private T data;
public GenericsClass(T data) {
this.data = data;
}
// method that return T type variable
public T getData() {
return this.data;
}
}
Output
Generic Class returns: 5 Generic Class returns: Java Programming
In the above example, we have created a generic class named GenericsClass. This class can be used to work with any type of data.
class GenericsClass<T> {...}
Here, T used inside the angle bracket <>
indicates the type parameter. Inside the Main
class, we have created two objects of GenericsClass
- intObj - Here, the type parameter T is replaced by
Integer
. Now, the GenericsClass works with integer data. - stringObj - Here, the type parameter T is replaced by
String
. Now, the GenericsClass works with string data.
Java Generics Method
Similar to the generics class, we can also create a method that can be used with any type of data. Such a class is known as Generics Method.
Here's is how we can create a generics method in Java:
Example: Create a Generics Method
class Main {
public static void main(String[] args) {
// initialize the class with Integer data
DemoClass demo = new DemoClass();
// generics method working with String
demo.<String>genericsMethod("Java Programming");
// generics method working with integer
demo.<Integer>genericsMethod(25);
}
}
class DemoClass {
// creae a generics method
public <T> void genericsMethod(T data) {
System.out.println("Generics Method:");
System.out.println("Data Passed: " + data);
}
}
Output
Generics Method: Data Passed: Java Programming Generics Method: Data Passed: 25
In the above example, we have created a generic method named genericsMethod.
public <T> void genericMethod(T data) {...}
Here, the type parameter <T>
is inserted after the modifier public
and before the return type void
.
We can call the generics method by placing the actual type <String>
and <Integer>
inside the bracket before the method name.
demo.<String>genericMethod("Java Programming");
demo.<Integer>genericMethod(25);
Note: We can call the generics method without including the type parameter. For example,
demo.genericsMethod("Java Programming");
In this case, the compiler can match the type parameter based on the value passed to the method.
Bounded Types
In general, the type parameter can accept any data types (except primitive types).
However, if we want to use generics for some specific types (such as accept data of number types) only, then we can use bounded types.
In the case of bound types, we use the extends
keyword. For example,
<T extends A>
This means T can only accept data that are subtypes of A.
Example: Bounded Types
class GenericsClass <T extends Number> {
public void display() {
System.out.println("This is a bounded type generics class.");
}
}
class Main {
public static void main(String[] args) {
// create an object of GenericsClass
GenericsClass<String> obj = new GenericsClass<>();
}
}
In the above example, we have created a class named GenericsClass. Notice the expression, notice the expression
<T extends Number>
Here, GenericsClass is created with bounded type. This means GenericsClass can only work with data types that are children of Number
(Integer
, Double
, and so on).
However, we have created an object of the generics class with String
. In this case, we will get the following error.
GenericsClass<String> obj = new GenericsClass<>(); ^ reason: inference variable T has incompatible bounds equality constraints: String lower bounds: Number where T is a type-variable: T extends Number declared in class GenericsClass
Advantages of Java Generics
1. Code Reusability
With the help of generics in Java, we can write code that will work with different types of data. For example,
public <T> void genericsMethod(T data) {...}
Here, we have created a generics method. This same method can be used to perform operations on integer data, string data, and so on.
2. Compile-time Type Checking
The type parameter of generics provides information about the type of data used in the generics code. For example,
// using Generics
GenericsClass<Integer> list = new GenericsClass<>();
Here, we know that GenericsClass is working with Integer
data only.
Now, if we try to pass data other than Integer to this class, the program will generate an error at compile time.
3. Used with Collections
The collections framework uses the concept of generics in Java. For example,
// creating a string type ArrayList
ArrayList<String> list1 = new ArrayList<>();
// creating a integer type ArrayList
ArrayList<Integer> list2 = new ArrayList<>();
In the above example, we have used the same ArrayList class to work with different types of data.
Similar to ArrayList
, other collections (LinkedList
, Queue
, Maps
, and so on) are also generic in Java.