Generics in Java is a mechanism that allows writing code for one type (say T) that is applicable for all types of data, instead of writing separate classes for each type.
Let’s understand it with the help of an example. Suppose we have class A with an instance variable x to store an Integer object.
class A { Integer x; }
This class can be used to store Integer type data only. We cannot use that instance variable to store String type object because the instance variable x is of type Integer. It can store only Integer type object.
To store String type value, we will have to rewrite this class as follows:
class A { String x; }
Now, can we store a Double class object into the instance variable x? Absolutely, it is not possible. To do so, again we will have to write another copy of class A, as:
class A { Double x; }
Like this, to store different types of data into a class, we will have to code the same class again and again by changing the data type of variable. This problem can be avoided if we use the generics feature.
Using generics, we can create a single class that will automatically work with all different types of data. Let us write a program to understand this statement.
// A generic class to store any type of object. // Here, T is a type parameter that will be replaced by a real (concrete) type when an object of type Myclass is created. public class Myclass<T> { T obj; // Declaring an object of type T. // Declare a constructor to initialize T type object. Myclass(T obj) { this.obj = obj; } // Declare an instance method that will return T type object. T getObject() { return obj; } } public class GenericsTest { public static void main(String[] args) { // Create an object of Integer class. Integer i = 20; // This is same as: Integer i = new Integer(20); // Create an object of Myclass and store Integer object into it. Myclass<Integer> obj = new Myclass<Integer>(i); // Call getObject() method to get Integer object. System.out.println("Stored value: " +obj.getObject()); // In the same way, we will use Myclass for storing Double object and retrieve it. Double d = 20.25; // Same as Double d = new Double(20.25); Myclass<Double> obj2 = new Myclass<Double>(d); System.out.println("Stored value: " +obj2.getObject()); // We can also use Myclass to store String type data also, as: String str = "Scientech Easy"; Myclass<String> obj3 = new Myclass<String>(str); System.out.println("Stored value: " +obj3.getObject()); } }
Output: Stored value: 20 Stored value: 20.25 Stored value: Scientech Easy
In this program, we have created Myclass objects three times and stored 3 different objects such as Integer, Double, and String objects into it.
So, it is clear that using generics concepts in java, we can write code for one type that can be used for all different types of data, instead of writing separate classes for each type.
Now observe that Integer is a wrapper class that is storing directly a value 20 in its variable. Actually, Java compiler creates an Integer class object internally and store the value 20, as follows:
Integer i = new Integer(20);
This feature is called autoboxing in java.
The feature “Generics” was introduced to Java in version 1.5. Generics are also known as parameterized types in java.
Parameterized types enable us to create classes, interfaces, and methods by specifying the type of data or objects as a parameter that it works with.
Why Generics?
Prior to Java 1.5, collection classes could hold any type of object. For example, add() method of an ArrayList class has the following declaration:
public boolean add(Object o) { // code to implement add method. }
We can pass any type of object to the add() method and ArrayList gladly took it. While retrieving an element from the collection, we need to type cast explicitly.
For example, suppose an ArrayList named carList with Car objects.
Car c = (Car)carList.get(0);
Here, the problem is that there is no guarantee that only Car objects could be added to the collection because add() method can accept any type of object.
To overcome this problem, Java language introduced a powerful feature “generics”. Using generics, we can declare ArrayList like this:
ArrayList<Car> carList = new ArrayList<Car>();
Here, the generic class can hold only Car types. Now, add() method can be declared like this:
public add(Car c) { // code to implement the add method. }
Thus, we can add only Car objects to the list. For retrieval of elements, get() method can be declared like this:
public Car get(int index) { // code to implement the get method. }
Thus, get() method returns Car objects. We do not have to cast the result into Car because Java compiler already knows that the object is Car.
Look at the below figure to understand generics better.
As you can observe in the above figure, without generics, we can gladly put anything into an ArrayList and Java compiler has no problem accepting it.
Whereas, with generics, we can put only specific elements in an ArrayList that will create a type-safe collection and no problem will occur at compile time instead of runtime.
Generic in Java
An element such as a class, interface, or method that works on parameterized type is called generic in java. In the above example program, T is the name of a type parameter.
Note that whenever a type parameter T is being declared, it must be specified within angle brackets. Since Myclass uses a type parameter, Myclass is a generic class, which is also called parameterized type.
The best example of generic is the collection classes. For example, we declare ArrayList with type parameter like ArrayList<String>, ArrayList<Integer>, ArrayList<Person>, etc.
Type Parameter in Generics
The type parameter is used as a place holder for the argument types. It can be any non-primitive type such as class, array, interface, or even another type parameter.
The name of type parameters is usually single and uppercase letters. Java has five kinds of type parameters. They are as:
- T denotes a Type.
- E denotes an Element.
- K denotes a Key
- N denotes a Number
- V denotes a Value.
A type parameter can be used for an argument and a return type for methods. It cannot be used to create an object, array, or in exception handling. The type parameter in generics cannot be static and cannot be used with instanceOf operator.
Features of Generics in Java
There are several important features of generics in java language that are as follows:
1. Java Generics adds type safety to the code. Using generics, we can hold only a single type of object. It does not allow to store other types of objects. Without Generics, we can store all types of objects. For example:
// Without generics. List list1 = new ArrayList(); list1.add(20); // adding integer number. list1.add("20"); // adding integer number in string form. // With generics, it is required to specify the object type that we need to store into it. // Here, we are declaring ArrayList to store integer objects only. List<Integer> list2 = new ArrayList<Integer>(); list2.add(10); // adding integer list2.add("10");// compile-time error
2. Generics allows dynamic binding.
3. It helps to detect and fix errors at compile time rather than runtime. A generic class or method permits to specify a type of object that
the class or method can work with.
If we try to use an incompatible object, Java compiler will detect that error. Hence, the problem will not occur at runtime. For example:
LinkedList<String> list = new LinkedList<String>(); list.add("hello"); list.add(32); // compile time error.
4. Generics helps to simplify the code by reducing ambiguity and eliminating the need for explicit typecasting.
For a non-generic collection, retrieval of elements needs an explicit type casting with the chances of ClassCastException exception. An example of a non-generic collection is as follows:
LinkedList list = new LinkedList(); list.add(1); Integer num = (Integer) list.get(0); // type casting.
Here, an object of LinkedList is created and an Integer type value is added to it. During retrieval of the value, explicit casting is performed to avoid exceptions.
With generics, we avoid the explicit type casting during retrieval of elements because the correctness of value is checked at compile time. An example of a generic collection is as follows:
LinkedList<Integer> list = new LinkedList<Integer>(); list.add(1); Integer num = list.get(0);
5. Java permits to define generic classes, interfaces, and methods from Java 1.5 version. Several classes and interfaces in the Java API were modified using generics.
For example, before Java 1.5 the java.lang.Comparable interface was defined as follows:
package java.lang; public interface Comparable { public int compareTo(Object o) }
After Java 1.5, it is modified as below:
package java.lang; public interface Comparable<T> { public int compareTo(T o) }
Here, <T> denotes a formal generic type, which is replaced later with a real type (i.e. concrete type) by Java compiler.
6. Java also allows declaring a generic constructor.
7. Java Generics work only with objects. i.e. While creating an object of a generic type, the type parameter must be a class type. It cannot be primitive types such as int, float, char, etc. For example:
Myclass<int> obj = new Myclass<int>(20); // Error, cannot use primitive type.
8. A generic type can have more than one type parameter. To define two or more type parameters in generic type, simply use a comma-separated list. For example:
class Myclass<T, V> { T obj1; V obj2; }
Advantages of Generics in Java
There are several advantages of using generics in java that are as follows:
1. The main advantage of generics is reusability. We can reuse the same code for different data types.
2. Another advantage of generics is type safety.
3. Using generics, we can eliminate typecasting.
4. It helps to avoid ClassCastxception at runtime.
Hope that this tutorial has covered almost all the important points related to generics in java with examples and its features. I hope that you will have understood the basic concepts of Generics.
Thanks for reading!!!
Next ⇒ Generic Class in Java⇒ Prev Next ⇒