Aditya Mahajan
Posted on August 8, 2024
What are Java Generics?
According to Javadocs
A generic type is a generic class or interface that is parameterized over types.
Now assuming you already know what is class and interface in Java(if not, please have a look here), we will go through what does parameterized over types means.
Now suppose we want to create a class, with a field name as fullName
and field type as Object
public class ThisIsNonGenericClass {
private Object fullName;
public ThisIsNonGenericClass(Object fullName) {
this.fullName = fullName;
}
public Object getFullName() {
return fullName;
}
public void setFullName(Object fullName) {
this.fullName = fullName;
}
}
Remember, here we are using Object
3 times.
Now if we want to print value for field fullName
, we need
casting to String
, since the compiler requires, explicit cast, for getting String
out of Object
.
public class ThisIsDevArticle {
public static void main(String[] args) {
ThisIsNonGenericClass thisIsNonGenericClass = new ThisIsNonGenericClass("AdityaMahajan");
String name = (String) thisIsNonGenericClass.getFullName();
System.out.println("This is name: " + name);
}
}
Lets introduce a parameter T
at class level. This can replace the type for field as well as the return type of methods. In this way we just introduced a parameter to a class and made the class type parameterized. Here T
has replaced Object
when compared to ThisIsNonGenericClass
public class ThisIsGenericClass<T> {
private T fullName;
public T getFullName() {
return fullName;
}
public ThisIsGenericClass(T fullName) {
this.fullName = fullName;
}
public void setFullName(T fullName) {
this.fullName = fullName;
}
}
Now lets try to print value for field fullName
.
public class ThisIsDevArticle {
public static void main(String[] args) {
ThisIsGenericClass<String> thisIsGenericClass = new ThisIsGenericClass<>("AdityaMahajan");
String name = thisIsGenericClass.getFullName();
}
}
What's the difference now?
By specifying the type as String here, ThisIsGenericClass<String> thisIsGenericClass
, we were able to tell the class what type to use and now the parameter is identified as String and the getFullName
is returning String. We even reduced the need of casting. Voila! This is Java Generics, and one advantage here is avoiding casting, by specifying the type argument as String
.
Quick Terminology here.
ThisIsGenericClass<T>
: T
here is called Type Parameter.
ThisIsGenericClass<String>
: String
here is called Type argument.
Another callout is the diamond operator <>
at the right can be empty as long as <>
has String
in the left.
ThisIsGenericClass<String> thisIsGenericClass = new ThisIsGenericClass<>("AdityaMahajan");
The compiler is smart enough to figure out itself when the class is instantiated. This is called type inference
, more on this here. It is equivalent to
ThisIsGenericClass<String> thisIsGenericClass = new ThisIsGenericClass<String>("AdityaMahajan");
We could always use multiple parameters in the class or interface like,
public class ThisIsGenericClassWithMutipleParameters<T1,T2> {
private T1 fullName;
public T1 getFullName() {
return fullName;
}
public void setFullName(T1 fullName) {
this.fullName = fullName;
}
private T2 age;
public T2 getAge() {
return age;
}
public void setAge(T2 age) {
this.age = age;
}
public ThisIsGenericClassWithMutipleParameters(T1 fullName, T2 age) {
this.fullName = fullName;
this.age = age;
}
}
We can use above with something like
ThisIsGenericClassWithMutipleParameters<String,Integer> thisIsGenericClass2
= new ThisIsGenericClassWithMutipleParameters<>("AdityaMahajan", 26);
Generic Methods
We can also use parameters in methods as well, and in next example, see how this can avoid runtime exception, catching compile time errors.
Suppose we have a method to calculate total length of string accepting List
as method argument.
public int getTotalLengthOfString(List names) {
int len = 0 ;
for(int i = 0; i<names.size(); i++){
String name = (String) names.get(i);
len += name.length();
}
return len;
}
If we do something like.
List names = new ArrayList<>();
names.add("Medvedev");
names.add("Tsitsipas");
System.out.println("This is total length : " + getTotalLengthOfString(names));
The output will be
This is total length : 17
However let say, for some reason we added another item, here an Integer
to the List
.
String nameWithoutCast = thisIsGenericClass.getFullName();
List names = new ArrayList<>();
names.add("Medvedev");
names.add("Tsitsipas");
names.add(32);
System.out.println("This is total length : " + getTotalLengthOfString(names));
And running the program led to a ClassCastException
, which occurs while we run a program, a child class of RuntimeException
. Following is the output.
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
How to avoid this?
Now lets update the method parameter from List
to List<String>
. Now doing this will also make us change the instantiation from
List names = new ArrayList<>()
to List<String> names = new ArrayList<>()
. And if you see, we dont even need casting.
public int getTotalLengthOfString(List<String> names) {
int len = 0 ;
for(int i =0; i<names.size(); i++){
len += name.length();
}
return len;
}
Now if someone try something like this,
List<String> names = new ArrayList<>();
names.add("Medvedev");
names.add("Tsitsipas");
names.add(32);
System.out.println("This is total length : " + getTotalLengthOfString(names));
compiler will complain and ask developer to fix it.
With this we made the compiler do tighter type checks, during compile time, and avoiding ClassCastException
during runtime. Another advantage of using Generics!
Some terminology here :
List<T> names
: Here we are using generic-type. And is a parameterized type with no argument.
List<String> names
: Here we are using generic-type. And is a parameterized type with argument String
.
List names
: Here the names is raw-type of List<T>
, with no parameters, no arguments.
That is all for introduction, let me know your thoughts in the comments.
Reference
- https://docs.oracle.com/javase/tutorial/java/generics/why.html and others in the same section
- https://www.youtube.com/watch?v=CKWw7J5MsyY by Jakob Jenkov
- https://docs.oracle.com/javase/tutorial/java/generics/QandE/generics-answers.html
Posted on August 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.