Generics in JAVA
Generics is one of the most important and hard to wrap head around features of the Java programming language. It was introduced in Java 1.5 version.
What is Generics?
Generally, a java class can take any type of Object. With generic, we can make a java Class, Interface, and method type-specific.
Benefits of Generics
- Generics provide type safety
- With Generics, we don’t need to perform type casting
- It provides reusability of our code
Let’s see how it helps us to make our code type-safe.
We can store any type of object in collections. At the time of retrieving it may lead to ClassCastException
or incompatible types
Error.
The above code will lead to ClassCastException
and Incompatible Type Error
.
Now applying Generics
we can force the ArrayList
to take only a specific type of object so that we don't need to typecast anymore and ClassCastException
will not occur.
We can use any object as a type for Generic but can’t use any primitive value.
Collection framework before and after Java 1.5
Before Generics (till Java 1.4) ArrayList class defined like this
class ArrayList{
add (Object o){...};
Object get(int index){...};
}
- The argument to the add() is an object. So we can insert any type of Object.
- The return type of get() method is Object. So at the time of retrieval, we have to perform typecasting.
After Java 1.5 the Generic version of ArrayList looks like this
class ArrayList<T>{
add(T t){...};
T get(int index){...};
}
At runtime type parameter ‘T’ will be replaced with the corresponding provided type.
For, ArrayList<String> obj = new ArrayList<String>
following populated version of code will be generated at runtime.
class ArrayList<String>{
add(String s){...};
String get(int index){...};
}
- Since the
add()
method takes String as a parameter so we can add only String object to the ArrayList. - The return type of the
get()
is String so we get only String object at the time of retrieval.
Java generic type parameter
Here is the naming convention provided by Java that helps you to understand the Java Generic. The most used type parameter names are:
E — Element (Used in Collections Framework for example Set)
K — Key (Used in Collections Framework for example Map)
N — Number
T — Type
V — Value (Used in Collections Framework for Map)
Custom Generic Class
We can make a class a Generic class by making it refer to any type (Integer, String, Character, Student, Object, etc.)
Below is an example of a Generic class
Output:
Type of the class: java.lang.Integer
Type of the class: java.lang.String
Type of the class: java.lang.Boolean
Custom generic class with multiple type parameter
Output:
100
Emrul
Wildcard in Generics (?)
- The question mark (?) is known as wildcard
- Wildcard represents an unknown type
- The use of wildcard is to control the type safety of the generic type.
- It restricts the unknown type to be specific.
- Wildcard also describes a family of type
Three types of wildcards in Java
- Unbounded wildcard <?>
- Upper bounded type “<? extends Type>”
- Lower bounded type “<? super Type>”
Unbounded type < ? >
It stands for the family of all types. That means while printCollection(List<Integer> list)
method can accept only Integer type list as an argument, printCollection(List<?> list)
can accept Integer, String, Boolean, Object, Student, etc. (basically all subclasses of Object) type list as an argument.
Output:
chandler bing ross geller
2 4 6 8 10
Monica 30 true
Upper bounded type “<? extends Type>”
Describes the family of all types that are a subtype of ‘Type’, including the type ‘Type’.
For example in printCollection(Collection<? extends Number> value)
method, only those types of Collection objects can be passed as an argument who are either Number
type or subtypes of Number like Integer, Double, Float. This type of wild card is used for reading purpose when object type is partially known.
Since we don’t know the actual runtime object type, we can’t perform add operation to a collection object using
unbounded
andupper bounded
generic wildcard. We can only perform read operation.
Lower bounded type “ <? super Type> “
Describes the family of all types that are the supertype of ‘Type’, including the type ‘Type’.
In addToCollection(Collection<? super Integer> value)
method only those types of objects of Collection can be passed who are either Integer
type of supertype of Integer like Number
, Object
. Lower bounded wildcards are mostly used for reading purpose.
Generic Methods
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter’s scope is limited to the method where it is declared.
Suppose, we want to write a method that takes an array and collection object and add the elements of the array into the collection.
static void fromArrayToCollection(Object[] arr, Collection<?> coll){for (Object obj : arr){
coll.add(obj); // compile time error
}
}
Since we can’t add objects to unknown types the above code will give us a compile time error.
The way to do deal with these problems is to use generic methods. Just like type declarations, method declarations can be generic — that is, parameterized by one or more type parameters.
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // Correct
}
}
Full example code,
We can also use multiple type parameter in a generic method just like the example below,
public class GenericMethod {
public static <T, E> void sampleMethod(T[] array, E ele ) {
System.out.println(Arrays.toString(array));
System.out.println(ele);
}
public static void main(String args[]) {
Integer [] intArray = {24, 56, 89, 75, 36};
String str = "hello";
sampleMethod(intArray, str);
}
}