Hello! In this article, we’ll learn about JavaGenerics, how to create generic classes and methods, its benefits, and how to use them to improve the quality of your code.

JavaGenerics was introduced in JDK5.0 to reduce bugs and add an extra layer of abstraction over types. Generics refer to parameterized types. JavaGenerics allows us to create a single class, interface, and method that can be used with different types of data (objects), which helps us reuse code.

Generic methods are methods written in a single method declaration that can be called with different types of arguments.

Generics also provide compile-time type safety, allowing programmers to catch invalid types at compile time.

“It is important to note that Generics does not apply to primitive types (int, Float, char, etc.).”

Why generics?

Suppose we want to create a list in Java to store integers; We could try to write:

List list = new LinkedList();
list.add(new Integer(1)); 
Integer i = list.iterator().next();
Copy the code

The compiler complains about the last line because it doesn’t know what data type to return, and it requires an explicit conversion:

Integer i = (Integer) list.iterator.next();
Copy the code

Now, this conversion can be annoying, because we know that the data type in this list is an Integer. Our code is messy. If a programmer makes an error in an explicit conversion, it can cause type-related runtime errors.

It’s much easier if programmers can express their intent to use a particular type, and if the compiler can make sure that type is correct. That’s the main idea behind generics.

Now let’s modify the code.

List<Integer> list = new LinkedList<>();
Copy the code

We narrow the specialization of this list to the Integer type by adding the operator <> that contains the types, that is, we specify the types to be saved in the list. The compiler can then enforce the type at compile time. This can add significant robustness and make the program easier to read.

We can create a class that can be used with any type of data. Such classes are called generic classes. Here’s how to create generic classes in Java:

class Main {
  public static void main(String[] args) {

    // initialize generic class with Integer data

    GenericsClass<Integer> intObj = new GenericsClass<>(5);
    System.out.println("Generic Class returns: " + intObj.getData());

    // initialize generic class with string data

    GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
    System.out.println("Generic Class returns: "+ stringObj.getData()); }}// create a generics class
class GenericsClass<T> {

  // variable of T type
  private T data;

  public GenericsClass(T data) {
    this.data = data;
  }

  // method that return T type variable
  public T getData(a) {
    return this.data; }}Copy the code

The output

Generic Class returns: 5
Generic Class returns: Java Programming
Copy the code

Here, we create a generic class called GenericsClass, which can be used to process any type of data.

class GenericsClass<T> {... }Copy the code

Here, T<> used in Angle brackets represents type parameters. In the main class, we create two objects of GenericsClass

IntObj- Here, the type parameter T is replaced by Integer. GenericsClass now handles integer data.

String Obj- Here, the type argument T is replaced with a String. GenericsClass now handles string data.

We can also create a method that can be used with any type of data. This class is called generic methods.

Here’s how to create generic classes in Java:

class Main {
  public static void main(String[] args) {

    // initialize the class with Integer data
    DemoClass demo = new DemoClass();

    // generics method working with String
    demo.<String>genericsMethod("Java Programming");

    // generics method working with integer
    demo.<Integer>genericsMethod(25); }}class DemoClass {

  // creae a generics method
  public <T> void genericsMethod(T data) {
    System.out.println("Generics Method:");
    System.out.println("Data Passed: "+ data); }}Copy the code

The output

Generics Method:
Data Passed: Java Programming
Generics Method:
Data Passed: 25
Copy the code

So, in the code above, we create a generic method named genericsMethod.

public <T> void genericMethod(T data) {... }Copy the code

We can invoke a generic method by placing the actual type and the actual type in parentheses before the method name.

demo.<String>genericMethod("Java Programming");

demo.<Integer>genericMethod(25);
Copy the code

We can call generic methods without type parameters. For example,

demo.genericsMethod("Java Programming");
Copy the code

Bounded type

Let’s look at bounded types. The Type parameter can accept any data Type except the basic Type.

Bounded means “limited” and we can restrict the types that can be accepted by methods.

If we want to use generics only for certain types (such as accepting numeric data or accepting string data), then we can use bounded types.

In the case of binding types, we use the EXTEND keyword.

<T extends A>
Copy the code

This means that T can only accept data of A subtype.

class GenericsClass <T extends Number> {

  public void display(a) {
    System.out.println("This is a bounded type generics class."); }}class Main {
  public static void main(String[] args) {

    // create an object of GenericsClass
    GenericsClass<String> obj = newGenericsClass<>(); }}Copy the code

In the code above, we created a class named GenericsClass. Notice that GenericsClass expressions are created with bounded types. This means that GenericsClass can only handle data types that belong to numeric subclasses (Integer, Double, and so on).

Generic advantage

It promotes code reuse: With the help of Java generics, we can write code that handles different types of data.

public <T> void genericsMethod(T data) {... }Copy the code

The above methods can be used to perform operations on integer data, string data, and so on.

Type safety

It is always better to understand problems in your code at compile time than to have it fail at run time

Generics are compile-time errors, not run-time errors.

// using Generics
GenericsClass<Integer> list = new GenericsClass<>();
Copy the code

Here, we know that GenericsClass only uses Integer data.

If we try to pass data other than Integer to this class, the program will generate an error at compile time.

It does not need to be cast separately

// Using generics converts run time exceptions into
// compile time exception.
import java.util.*;

class Test
{
    public static void main(String[] args)
    {
        // Creating a an ArrayList with String specified
        ArrayList <String> al = new ArrayList<String> ();

        al.add("Success");
        al.add("Gideon");


        String s1 = (String)al.get(0);
        String s2 = (String)al.get(1); }}Copy the code

If we didn’t use generics, in the example above, we would have to type data every time we retrieved it from an ArrayList. In fact, there is a lot of pressure to cast on every retrieval operation. If we already know that our list contains only string data, we don’t need to enter it every time.

// We don't need to typecast individual members of ArrayList
import java.util.*;

class Test
{
    public static void main(String[] args)
    {
        // Creating a an ArrayList with String specified
        ArrayList <String> al = new ArrayList<String> ();

        al.add("Success");
        al.add("Gideon");

        // Typecasting is not needed
        String s1 = al.get(0);
        String s2 = al.get(1); }}Copy the code

conclusion

Generics are a powerful addition to the Java language because they make the programmer’s job easier and less error-prone. Generics perform type correctness at compile time, and it enables the implementation of generic algorithms without additional overhead to our application.