Generics are a feature of generic programming which was introduced in Java 5. This allows a type or method to work with various object types while providing compile-time type safety. For example, collection framework supports generics. We can use Hashset, ArrayList, HashMap etc. to store various type of objects, and while retrieving the data we don’t need to type cast.
In Java 5, java rewrote the collection framework to incorporate the generics.
Here is an example of ArrayList without using generics:
List names = new ArrayList();
names.add("Manish");
String name = (String) names.get(0); //Type casting when retrieving the element from the array list
Integer number = (Integer) names.get(0); //Run-time error, ClassCastException will be thrown.
When we use the ArrayList without generics, will have to type cast when we retrieve the element from the ArrayList, as the program doesn’t know the type of element stored in ArrayList. Also, when we try to type cast the element in the wrong way as I did in the code above, trying to type cast the element to Integer it will throw run-time ClassCastException error. Without generics, we only get the errors at the run-time not at compile time.
We can rewrite the code using generics as follows:
List names = new ArrayList();
names.add("Manish");
String name = names.get(0); //No type casting is required.
Creating custom Java generic Class:
We can create our own generic class. A generic type is a class or interface that is parameterized over types, we use angle bracket (<>) to specify the type parameter.
Note: Type argument can’t be of a primitive type.
public class GenericExample {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
class GenericTest{
public static void main(String[] args) {
//Instance with String type
GenericExample genericExample = new GenericExample<>();
genericExample.setT("Test");
//Instance with Integer type
GenericExample genericExample1 = new GenericExample<>();
genericExample1.setT(100);
//Type argument can't be primitive type.
}
}
Class with multiple type parameters:
public class GenericExample {
private T t;
private U u;
public GenericExample(T t, U u) {
this.t = t;
this.u = u;
}
}
class GenericTest{
public static void main(String[] args) {
//Instance with String type
GenericExample genericExample = new GenericExample<>("Test", 100);
}
}
Generic Method
We can also create the generic method or constructor, no need to parameterize the whole class.
class GenericMethodTest{
static void genericMethod(T t){
System.out.println(t);
}
public static void main(String[] args) {
GenericMethodTest.genericMethod("Test");
GenericMethodTest.genericMethod("Test 2"); //Using type interface
}
}
In the above code, we can specify the type when calling the method or can just call like a normal method without specifying any type, JVM will do it for you. This feature is called type interface.
Java generics bounded type parameters:
If we want to restrict the type of objects which can be used as parametrized type:
class GenericBound {
private T t;
public GenericBound(T t) {
this.t = t;
}
}
Java generic type naming convention (Best practices)
As per the Java best practices, type parameter should be single and uppercase letters. Following are the commonly used type parameters:
- T – Type
- E – Element (Used extensively in collection framework)
- N – Number
- K – Key
- V – Value
- S,U,V etc. – 2nd, 3rd, 4th types