Moussa SOW
Posted on August 4, 2021
Why we use generics?
Let's start with an example where we want to create a list in Java. A simple way to do this is by using the ArrayList class.
I ask Java to create a variable named persons which is an empty ArrayList.
I plan to create a guest list for my presentation and to send an email to all the people in my list π.
ArrayList class allows me to easily add and retrieve values in my list with add and get methods.
So, letβs do that π
Surprise, surprise π
The compiler starts screaming. We can't get the first element of our list.
What do you think the problem is? π€
We are sure that what we put in our list is of type String. So why can't I retrieve it using the get method?
This leads me wonder two things. What does the add method take as parameters and what does the get method of ArrayList return?
Here Java does not know if we want to create a list of Animal or a list of String. So, since all classes inherit from the Object class, the add method will wait for an object in parameter, as well as the get method will return an object.
Indeed, the way we have proceeded does not allow us to know what type of object we want to create.
This means two things:
- The first is that we can add any type of object to our list
- The second is that we will have to cast the value returned by the get method
At line 10, I added 99.99 to my list of people. The compiler does not complain, because the add method expects an Object as parameter.
Also, at line 13 the get method returns an object. And I say to the compiler trust me, you can cast it to string because the inserted value was of type string.
The compiler doesn't complain, because yes, it is possible to convert an object into a string.
What do you think will happen after execution? π€
Go 1, 2, 3 ππΏββοΈ ππΏββοΈ
The execution ends with a runtime exception as shown in the figure below.
This is a ClassCastException which clearly tells us here that we can not convert a Double into a String.
We face two problems π©.
The first one is we have to convert all types of retrieved values before assigning them.
This is quite a big job. Let's suppose that we have to manage the list of fans that the Iba Mar Diop stadium hosts. Each time we retrieve a value from the list, we have to cast it.
But also, we do not control the types of data that will be added to our list, which can make our application crash.
Wait the first time, I created lists I didn't have these errors.
What I was doing back then.
Thinking one sec π π.
Ah, I remember π. In fact, instead of saying the variable persons is an ArrayList, it is possible to say that it is an ArrayList of String like this:
We notice here that the compiler complains at line 10.
Indeed, the compiler now knows that the type of the data to be added to the list is String. So if we decide to add a Double to the persons list, the compiler warns us that the contract is not respected.
Now I have a question for you. What happens if I remove the cast?
I'm sure you've guessed it. It works.
Because the compiler knows that the variable named persons is a list of Strings. So, if we call the get method on this variable, the returned value will be of type String.
It's logical right? !!
Our problems are now solved ππΏ.
We have given the compiler the power to check if the given type we want to add to our list is the expected one. And we don't need to cast the value to be retrieved anymore.
This means that when we want to have a list of grades of the students of the class, we will have to create a list whose values will be of double type for example.
This is the concept of generics π€©.
With Generics, we can create an object (here ArrayList) which will allow us to store elements of the same type (double for the students' grades).
Let's do this little exercise: create a list with double values to store the grades of my students, just to make sure we got it right.
But why is the compiler complaining? I did the same thing as before, no π€·πΏββοΈ.
String and double are types that do exist in Java.The difference is that double is a primitive type while generics do not work with primitive types.
Only reference types can be used as generic parameters.
Does this mean that we can't have a list of integers or a double type?
Of course not.
It is important to know that every primitive type can be wrapped in an object of a class called Wrapper.
I assume you know the concept of autoboxing.
Introduced in Java 5, autoboxing is the automatic conversion of primitive types into objects of their corresponding wrapper class.
For example, converting int to Integer, long to Long, double to Double, etc.
Autoboxing now allows us, instead of passing a primitive type, to pass a wrapper class like this.
Create our own generic
We have seen that when we use ArrayList, we pass it a parameter to solve the problems we had.
Before creating our own generic we are going to see how the ArrayList class is created. To do this, we'll take a look about the documentation.
Here <E>
denotes the type parameter of the ArrayList class. The type parameter defines that it can refer to any type (like String, Integer, Salon, etc).
And when we create a variable "names":
ArrayList<String> names
, it is as if in the ArrayList class the element E was replaced by String in the whole class. An association is thus made between the variable and the type.
This is why the compiler will now know which type is associated with the created variable and will be able to check, for example, the type of the values that will be passed in parameter.
π‘ An interesting remark to make is that this type is not present at runtime. Indeed, once our java code is transformed into byte code, all the generic types are removed. This is called type erasure.
I hope you now understand how generics work. Let's build our own generic.
What I have in mind
In the village of Toubacouta, I want to present two senegalese fashion brands, one of clothing (Master Design) and one of leather goods (Bouswari).
For this I have rented two salons. And I want the designer of each brand to be able to install his products in the room reserved for him.
We will assume that we have created two classes MasterDesign and Bouswari.
We will now create our Salon class like this:
<E>
means that Salon expects to be passed a type (in this case, a brand). This will tell the compiler what brand the items in this salon will be.
For example, here we have created a salon that will be filled with Master Design items and at line 5 we try to decorate this salon with a Bouswari product.
We now have our two salons, each with items from a unique brand.
It should also be noted that if in the future we decide to incorporate new brands, creating their salon will be super easy.
Instead of adding items via the setter, it is possible to initialize our salon using a constructor.
I'll share this next piece of code with you and let you tell me why I'm able to put Bouswari articles in the Master Design room. And why the compiler complains at line 4.
I'm sure you guessed right ππΏ.
On line 3, we assign a raw type to a parameterized type. Of course, Java allows us to do this. That's why the compiler doesn't report any errors.
A raw type is the name of a generic class or interface without any type arguments.
π‘ It should be noted that when the type is omitted between the angle brackets, the Bouswari type of the left part is implicitly taken. Therefore, on the right side of the equality we expect an item of type Bouswari as a parameter. Thus, the compiler is confused when a Master Design item is passed as a parameter. That's why it underlines it.
I hope now you understand what generics is and how it works.
I will continue the second series on generics by talking about inheritance and other concepts so that we have a thorough understanding of generics.
If you have any questions, feel free to reach me out βπΏ
- Twitter: _sowmoussa
- Github: sowmoussa
Posted on August 4, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.