Generics in Java — Have no fear, Generics is here

T.Surkis
AndroidPub
Published in
6 min readMay 16, 2017

--

http://www.clipartbest.com/clipart-RcGG5agpi

Generics is one of the best tools you could have in your tool belt. Whether you used generics before or were too afraid to, I welcome you to this generic overview (pun intended).

Methods

We will assume that Arrays.toString() does not exist and we need an integer array printing method:

If a double array printing method is required, a simple copy paste action (mastery of software developers) followed by variable type name changes will result in:

Similarly, if a new printing method for string arrays is needed, we could copy-paste and change the variable name types. However, a close look at both methods reveals similarities. The only difference between the two are the places in which we declare the variables.

public void printArray(int [] array) {public void printArray(double [] array) {for(Integer printObject : array) {for(Double printObject : array) {

In these cases, generic method types roll up their sleeves. Using a generic method type we can create a general method that can accept any variable type during usage.

public void printArray(ArrayType [] array) {for(ArrayType printObject : array) {

Similar to a regular method, a generic method has a special place to declare the variable types it will receive. The declaration will take place before the return type and will appear with an opening ‘<’ sign followed by a closing ‘>’ sign. Therefore, the correct way to write the method is as followed:

The explicit way to use a generic method is:

Luckily for us, the variable type recognition is being done by recognizing the variable type passed inside. Consequently, the common way to use a generic method is in an implicit form.

Due to generics API restrictions, only variable types that extend object types can be used as variable types. Therefore, primitives we used before will now be used in their class form.

Of course, generic methods are not restricted to a single variable type.
Just like a method, we can add as many variable types as we want, separated by a comma.

For example, we can create a dual array printing method. For simplicity sake, let’s assume that the received arrays are not null and are the same size.

Classes

What if we wanted that class to hold a generic class member?

public class ArrayWrapper {
private ArrayType [] array;
public <ArrayType> void printAnyArray() {
...
}
}

Just as a generic method has its place for variable types, so does a class. The declaration will be after the class name and will appear with an opening ‘<’ sign followed by a closing ‘>’ sign.
In the next example, we wrap an array with a generic class and provide a printing method. Since the array used in the method is the same type as the variable type declared in the class, we can drop the generic declaration from the method.

Using a generic class resembles the generic method usage in that it has two ways — the explicit way and the implicit way.

The explicit way is:

The implicit way is:

Unlike generic methods, when using classes, I strongly advise using the explicit way.

Why?

Take a look at the next example:

The default constructor is employed. Therefore, the variable type of the class is only decided when the setter method is used in runtime.

What would happen if the class setter method is used far from its instantiation? Looking at the creation alone would not provide an answer since the variable type is not declared there. We would need to track the first usage of the setter method. Whereas, if the explicit way were used, a look at the instantiation would be enough.

Of course, all the previous rules apply when we add a generic method with a different variable type than our classes variable type. In the next example, our class has a generic method that receives an array and prints it alongside the array of the class.

Inheritance & Implementation

I am an Android developer, and for my personal project, I was in need of a database. Not keen to write the implementation myself, my research has brought me up to GreenDAO.

One of my POJO classes has an ArrayList of Integer types as a class member. However, GreenDAO, just like other database solutions, offers only a limited set of types — ArrayList is not one of them.
The answer comes in the form of a generic interface.

This code was copied from the GreenDAO official GitHub Repository:

P represents a value in the POJO class.
D represents the POJO value in its database form.
The implementing class has to create a conversion in both directions.

I have decided save my ArrayList of Integer types in the database as a String. I chose String because of its simplicity. The code itself will explain the conversion, but it is not the purpose of this article.

How do we apply our Generics API knowledge?
Our POJO member class value is an ArrayList of Integer types. Therefore it will be placed instead of the P.
The ArrayList of Integer types will be translated to a String. Therefore it will be placed instead of the D.

... implements PropertyConverter<ArrayList<Integer>, String>

Wait, what? A generic inception? Exactly!
The Generics API allows us to insert generic types as generic types (it can go even further depending on how crazy you are).

Let’s take it a step further.

Before the class can be used, it needs to be tested (using JUnit 4).
The testing algorithm is pretty basic and is not part of this article, but this is the gist of it:

testEntityConversion(entityValue, converter) {
databaseValue = converter.convertToDatabaseValue(entityValue)
newEntityValue = converter.convertToEntityValue(databaseValue) assertEquals(entityValue, newEntityValue)
}
testDatabaseConversion(databaseValue, converter) {
entityValue = converter.convertToEntityValue(databaseValue)
newDatabaseValue = converter.convertToDatabaseValue(entityValue) assertEquals(databaseValue, newDatabaseValue)
}

The inserted value is converted to its other value and back. Only when the result equals the inserted value, the test has passed and the conversion works flawlessly.

Just after I wrote the code in my project, I have created another converter class. As a result, another test class was needed. However, I detected a pattern.
Sounds familiar? You might have already noticed that the written algorithm is very general.
You have guessed correctly, we can create a generic converter test class.

How do we start?

Before a single line of code is even written, we need to analyze the components:

  • Entity value.
  • Database value.
  • Converter.

Each of the components can be replaced with a general one without breaking the written above algorithm. The converter test class general structure will be as followed:

public class GeneralConverterTest<EntityType, DatabaseType, ConverterType>

It might seem as if we are done. But, if we do the modifications, we will run into a problem.

converter.convertToEntityValue(...)converter.convertToDatabaseValue(...)

The ConverterType we added is expected to extend the Object class. However, the Object class does not have these methods.
In some way, we need to declare in the generic class that a class type implementing PropertyConverter<P, D> is the only class type we can pass.

How?

Generics API allows us to add the ‘extends’ keyword inside the declaration. Oddly enough, it applies to classes that implement an interface or extend a class.

...<..., ..., ConverterType extends PropertyConverter>

We can now use the ConverterType methods. However, we did not declare the variable types the converter methods receive.
Just as we extended the PropertyConverter<P, D> class and passed down the variable types we add the variable types to the PropertyConverter<P, D>.

...<...,...,ConverterType extends PropertyConverter<EntityType, DatabaseType>>

The full declaration means that the EntityType, DatabaseType and a ConverterType that implements an interface with EntityType and DatabaseType as variable types are expected as variable types.

Conclusion

Generic API is quite flexible and can be adapted to various scenarios. If you are detecting a repeating pattern or feel like you are copy-pasting don’t forget about generics — it may come in handy!

All the examples in this article can be found in here:

Click the ❤ button to let me know if you enjoyed the article and follow me on Medium.

--

--

T.Surkis
AndroidPub

A Flutter developer by day, a technology enthusiast by night.