You are currently viewing Generics: Basics and Use Cases in Java

Generics: Basics and Use Cases in Java

1. Introduction

Generics in Java were introduced in JDK 5 to provide stronger type checks at compile time and to support generic programming. By allowing types to be parameters when defining classes, interfaces, and methods, generics promote code reusability and type safety.

According to Oracle, “Generics add stability to your code by making more of your bugs detectable at compile time.” This quote underscores the primary motivation behind the adoption of generics in modern Java applications.

In this article, we explore the core concepts of Java generics and examine practical use cases. If you’re already familiar with collections in Java, this will serve as a logical next step.

2. Why Use Generics?

There are several reasons for using generics in Java:

  • Type safety: They allow errors to be detected at compile time rather than runtime.
  • Code reusability: You can write a method or class that works with any type.
  • Elimination of type casting: When using generics, explicit type casting is unnecessary.
  • Cleaner APIs: Generic code is often easier to read and maintain.

For example, using raw types in collections can lead to a runtime ClassCastException, while generics eliminate that risk by enforcing type constraints during compilation.

3. Basic Syntax of Generics

The basic syntax of generics uses angle brackets (<>) to define type parameters. Typically, a single uppercase letter is used as the type parameter name — commonly T for “type”, E for “element”, and so on.

Example: A Simple Generic Class

The following example defines a simple generic class Box<T> that can store and return an object of type T.

public class Box&lt;T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }
}

This class enables you to create type-safe containers without duplicating code for each data type.

4. Common Use Cases

4.1. Generic Classes

As shown in the previous section, generic classes are used to define classes that can operate on objects of various types while maintaining type safety. Here’s how to use the Box class:

Box&lt;String> stringBox = new Box&lt;>();
stringBox.setContent("Hello Generics");
System.out.println("Box contains: " + stringBox.getContent());

This ensures that the box only contains String objects.

4.2. Generic Methods

Generic methods allow you to define a method with its own type parameters, independent of the class’s generic parameters.

public static &lt;T> void printArray(T[] array) {
    for (T item : array) {
        System.out.println(item);
    }
}

This method can be used with any array type, such as Integer[], String[], or Double[].

4.3. Bounded Type Parameters

You can restrict the type parameter to a specific class or interface using bounded type parameters.

public static &lt;T extends Number> double sum(T a, T b) {
    return a.doubleValue() + b.doubleValue();
}

This method accepts only subclasses of Number such as Integer, Double, and Float, ensuring numeric operations are valid.

4.4. Wildcards

Wildcards provide flexibility when working with generics, especially when the exact type is unknown.

public static void printList(List&lt;?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

The <?> denotes an unknown type, allowing the method to accept lists of any object type.

5. Limitations of Generics

While generics are powerful, they come with a few limitations:

  • You cannot instantiate generic types with primitive types (e.g., List<int> is invalid).
  • Java generics use type erasure, meaning type parameters are not retained at runtime.
  • You cannot create arrays of generic types.

These limitations often require workarounds, such as using wrapper classes like Integer instead of int.

6. Conclusion

Generics are a cornerstone of modern Java development, enhancing both type safety and code flexibility. By mastering them, developers can write more robust and reusable code.

You can find the complete code of this article here in GitHub.

Noel Kamphoa

Experienced software engineer with expertise in Telecom, Payroll, and Banking. Now Senior Software Engineer at Societe Generale Paris.