Java Generics are used to enforce type-safety in a program. A type-safe program does not have compilation and run-time errors that can result from using incorrect data types.
We’ll be covering the following topics in this tutorial:
What is Generics in Java?
Generics in Java are similar to templates in C++. Generics is a method that refers to a set of language features linked to the definition and application of Common types and methods. Java Generic methods differ from common data types and methods. Before Generics, we utilized the collection to save any objects i.e., non-generic. Now, Generics induce the Java programmer to store a particular type of object. Generic programming provides the facility to get a set of related methods and announce one method that supports almost any legitimate type.
For example, classes such as HashSet, ArrayList, HashMap, etc. use generics exceptionally well. We can use them for almost any type.
Generics are released in Java 5, and it’s a center for generic programming in the Java programming language. With the support of Generics in Java, you can define a type or method to an object of different, which is utilized for compile-time security checking. Philip Wadler produced Generic Java from the year 1998.
Following is a simple example of Generics in Java:
List < String > list = new ArrayList < String > (); list.add("Hello World"); Integer in = list.get(0); // (type error) Compile time error
From the above code, we’ve utilized the Generics while specifying the array list. The type of to be kept in the array listing is merely the String. If we attempt to typecast the data into Integer, it’ll provide a compile-time error. This manner compiler will have the ability to find the mistake in the compile-time on your program and remove the errors associated with invalid types.
Now that you know what Generics in Java, let’s move further and understand why you need Java Generics.
Recall from our earlier discussion that the ArrayList class is used to create an array to store elements. The size of the array changes automatically as elements are added or removed from it. First, we describe a potential problem with using ArrayList without generics. Let us create an array list as shown here:
ArrayList todoList = new ArrayList();
Suppose that we would like to use this list to keep track of the weekly chores associated with caring for a pet cat. We can add elements to this list using the add method in ArrayList:
todoList.add("Buy gourmet cat food"); todoList.add("See funny cat pictures online"); todoList.add("walk the cat");
All of the elements added to the list are of type String. If you put the above code in the main method and run it, it would run correctly without any errors. Recall, however, that the add method is defined as shown here:
public boolean add(Object obj) – a method that adds the object obj to the end of the list.
Therefore, you able to add an object of any type (say, Box) to todoList using this method:
todoList.add(new Box());
There are two problems with this approach. First, it is odd that a to-do list contains an element that is not of type String. Second-and worse-is that it can cause an error at run time.
Suppose that we would like to display the items in the to-do list. It can do with ArrayList’s get method:
public Object get(int index) – a method that returns the element at the given index.
The element returned is of type Object; thus it must be cast back to its original type. A programmer has written this statement to read the fourth item in todoList, expecting it to be of type String:
String s = (String) todoList.get(3); System.out.println(s);
Running the program now results in the following ClassCastException:
Exception in thread "main" java.lang.ClassCastException: genericsandcollections.Box cannot be cast to java.lang.String at genericsandcollections.ListDemo.main(ListDemo.java:14)
The problem is that the fourth element at index 3 is an instance of Box,and it cannot be cast to type String after it is retrieved using the get method. To prevent this error, we will create a list that can only hold elements of type String. This is done as follows:
ArrayList<String> newList = new ArrayList<String>();
Note that we have inserted the type String within angle brackets (<, >). This declaration tells the compiler that newList can only store elements of type String. The advantage of doing this is that if the programmer tried to insert an element of a different type in this list, it would be caught during compilation and thus prevent a run-time error. Generics makes this type of declaration possible. With generics, we can specify the type of elements that will be stored in a particular collection before it is created.
Let us examine the documentation for the ArrayList class with generics. This class is defined to be generic by adding the letter E within angle brackets:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess,Cloneable, Serializable { // code .. }
The letter E is a placeholder for the actual element type that will be defined by the programmer. E is known as the type parameter of the ArrayList class. By convention, the type parameter is a single uppercase letter, and is defined to be E for the collection classes. For example, this declares an ArrayList called myPictures that can hold elements of type Picture:
ArrayList<Picture> myPictures;
The type parameter E is replaced by Picture in ArrayList. Here, Picture is called the type argument to the ArrayList class. When you create myPictures, you must again specify that it store elements of type Picture:
myPictures = new ArrayList<Picture>();
The type argument Picture is inserted within angle brackets between ArrayList and the parentheses. Now, myPictures can only hold elements of class Picture (and its subclasses). Note that you cannot create an object of type parameter E because it is not an actual data type.
The type argument can be a class type or interface type, but it cannot be a primitive type. This statement results in a compilation error because it creates an ArrayList with a primitive type argument of int:
ArrayList<int> newList = newArrayList<int>(); // error!
However, you can use a wrapper class, such as Integer, in place of the primitive type.
Generic methods are declared using type parameters. In ArrayList, methods add and get are defined to be generic using the type parameter E:
public boolean add(E e) – a method that adds the element e to the end of the list.
public E get(int index) – a method that returns the element at the specified index in the list.
The methods add and get have a type parameter E instead of Object. After the ArrayList instance has been created, you can use these methods just as you would use any other method in a class. Note that you do not need to cast the value returned by the get method because it is of the data type represented by E and not Object.
To see how the generic methods are called, let us create todoList using genencs:
ArrayList<String> todoList = newArrayList<String>();
This replaces the type parameter E with String in todoList’s methods. Therefore, this call to add works correctly:
todoLisLadd("Buy gounnet cat food");
However, the following statement results in a compilation error because add can take only a String argument:
todoList.add(new Box()); // error
The get method retrieves the objects as type String instead of Object:
String s = todoList.get(2);
Note that a cast to String is not needed now because the object retrieved is of type String.
What is Use of Generics In Java?
Generics are introduced in Java 5 to Empower types (classes and interfaces) to Function as parameters when defining classes, interfaces, and methods. The Java compiler implements them as a front-end conversion known as the erasure. The uses of generic as follows:
Elimination of Type Casting
Generics offer the type checking at compile time. Finding bugs in compile-time may spare time for debugging java Program since compile-time bugs are a lot simpler to discover and fix. Should you use generics, you don’t need to execute the typecasting explicitly. Java compiler implements significant type checking if you use generics on your code and reveals errors in the event the code violates the type of security. You are thus removing the risk of ClassCastException.
Before use Generics Methods
List list = new ArrayList(); list.add("Before use Generics method"); String str = (String) list.get(0);
In the above case we can see typecasting in last line.
After use Generics Methods
List < String> list = new ArrayList <String>(); list.add("After use Generics method"); String str = list.get(0);
Stronger type checking at compile-time
They are finding bugs in compile-time May Spare time for debugging java program since compile-time bugs are much easier to Locate And mend. Java compiler applies significant type checking to generic code and problems mistakes when the code violates type security.
Example
List < String> list = new ArrayList <String>(); list.add("After use Generics method"); String str = list.get(0);
The compiler is responsible for understanding Generics at compile time. The above code has been checked at compile-time, so it’s ensured that no difficulty will happen at runtime. They are allowing developers to implement generic algorithms.
By using generics, developers may implement generic algorithms that operate on collections of distinct types, may be customized, and are type-safe and easier to read.