An introduction to using generics in Java

I recently worked on a few benchmarks for a database and while coding it used the power of generics to write as little code as possible. In the benchmark, I had two different DAO classes and corresponding Domain Objects that were identical, with the exception of the ID field. I could write everything twice, but there is no fun in that. Instead, I wrote generic classes that accepted the ID as a type parameter.

Generics is one of the most powerful features in Java and it will help you organize your code better, reduce work and improve code quality, as long as you use it correctly. Chances are that you already used generics before since it is widely used in the Java standard API. If you worked with Lists, Maps or Sets, you used generics since all three rely on it. At the end you will find simple and concrete examples without all the chit-chat.

Generic class in Java

Let’s start simple. We will be creating a system that models components for a PC. We have multiple components, like CPU, RAM, or motherboard, and we want to store multiple items, each with its own characteristics. Let us model the CPU first. We have a name, a brand, and an architecture (ARM or X86). Based on the architecture, the CPU can call its underlying class to do certain operations. We could model our CPU like this:

public class CPU<ARCHITECTURE> implements PcComponent {
    private ARCHITECTURE architecture;
    private String name;
    private String brand;
}

I know what you are thinking. Why can’t I just have the architecture as a normal object, maybe a CpuArchitecture interface, and be done with it. In most cases, you could use this approach without any problems. Now, let’s assume that each architecture has its own set of operations/methods and the CPU can’t change the architecture once it is defined. We can just set its underlying object that does the operation.

Now, when we create a CPU, we must explicitly say what architecture it has. Furthermore, we can retrieve the architecture from the object and use it, with the editor and compiler knowing the exact type that it has. If we assume that X86 has an add() method but ARM only has a move() operation, we won’t have any confusion as to what operations we can use when retrieving the architecture.

PU<ARM> snapdragon = new CPU<>();
CPU<X86> amd = new CPU<>();

X86 x86 = amd.getArchitecture();
x86.add("a", "b", "c");

If we would try to call move(), a method that is only available on the ARM part, we will get an error right away.

Restricting the generic

In the current implementation, we can specify any object type for the generic. You can as easily have a CPU with a String architecture. What if you want to restrict it so that only TRUE architectures are allowed. For this, let’s create our CpuArchitecture interface and have both X86 and ARM implement it. Now, we can modify our CPU class so that it will only also true CPU architectures for the generic like this:

public class CPU<ARCHITECTURE extends CpuArchitecture> implements PcComponent {
    private ARCHITECTURE architecture;
    private String name;
    private String brand;
}

Generic methods in Java

Now that we learned how to write a generic class, let’s look at generic methods. Obviously, a class that has a generic type can have a method that returns that generic type. For example, the ARCHITECTURE can be returned in a getArchitecture() method in our CPU class, as shown below:

public ARCHITECTURE getArchitecture() {
    return architecture;
}

But what if you don’t have a generic class? There are situations where a normal class has a generic parameter and it should return an object of that type. For example, the RAM class has a serial generation method that receives two inputs, the serial prefix, and the component number, and returns a computed serial. The prefix can be any Serializable object.

public class RAM implements PcComponent {
    public <T extends Serializable> String getSerial(T serialPrefix, int componentNumber) {
        return serialPrefix.toString() + componentNumber;
    }
}

Conclusion

Java offers an easy way to use generic data types and the functionalities that it offers will help you code better and more flexible code. Furthermore, it will reduce code duplication without making things complicated or requiring a lot of implementation of time. Below You can find easy examples.

Java Generics Examples

Java Geric Class
// Generic Class in Java
public class MyClass<T> {
    private T myGenericVariable;
}
Java Bounded Generic
// Bounded Generic Class in Java
public class MyClass<T extends Number> {
    private T myGenericVariable;
}
Java Generic Method
// Generic Method in Java
public <T> String getSerial(T serial) {
    return "Serial No: " + serialPrefix;
}
Java Widlcard Generic
// Widlcard Generic in java
public void printNumbers(List<? extends Number> numbers) {
    ...
}


Source link