Generics in Java allow for the use of types such as Integer, String, and user-defined types as parameters for methods, classes, and interfaces. Many standard classes in Java, such as HashSet, ArrayList, and HashMap, make use of generics.
The primary advantage of generics is that they allow for the creation of code that can be used with any non-primitive type, without needing to rewrite the code for each type. For example, a single generic function can be written to add two numbers for multiple data types like Integer, Double, etc. Similarly, a single generic function can be written to print the elements of an array of any data type. During compile time, the specific data type to be used with the generic function is passed as a parameter.
Java Collections extensively use generics. All the classes, interfaces, and utility functions are generic functions and generic classes. For example, the ArrayList class can be used to work on various types of data types, including Integer, Character, or user-defined data types such as Student.
Generics can be implemented in classes, such as ArrayList, LinkedList, Stack, Vector, or interfaces such as Set and Map, and they include functions like binarySearch(), sort(), max(), and min().
Generics make it easier to write type-safe code. Using generics ensures that errors appear at compile time rather than at runtime. This can save a lot of time and effort, as it is always better to know the problems in your code at compile time rather than making your code fail at runtime.
For example, let's consider two different examples. In the first example, no generic function is used, and the code throws a runtime exception.
typescriptimport java.util.*;
class Pair {
Object x;
Object y;
}
class Test {
public static void main(String[] args) {
Pair p = new Pair();
p.x = 12; // compiles fine because p being an object accepts integer
p.y = "GfG"; // compiles fine because p being an object accepts string
String str = (String) p.x; // This throws a ClassCastException as p.x was an integer and cannot be casted to a string
}
}
Output:
vbnetException in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Test.main(File.java:16)
In contrast, the second example uses a generic function, which shows compile-time errors:
typescriptimport java.util.*;
class Pair<T, S> {
T x;
S y;
}
class Test {
public static void main(String[] args) {
Pair<Integer, String> p = new Pair<Integer, String>();
p.x = 12; // compiles fine because p being an object accepts integer
p.y = "GfG"; // compiles fine because p being an object accepts string
String str = (String) p.x; // This shows a compiler error as p.x was an integer and cannot be casted to a string
}
}
Output:
vbnetprog.java:28: error: incompatible types: Integer cannot be converted to String
String str = (String) p.x;
^
1 error
Let's take a look at another example that demonstrates how a generic function works:
javaimport java.util.*;
class Test {
public static<T> int count(T arr[], T x) {
int res = 0;
for(T e: arr) {
if (e.equals(x))
res++;
}
return res;
}
public static