Table of contents
1.
Introduction
2.
What are Generics?
3.
Significance of Generics
4.
Why Were Generics Introduced in Java?
5.
Role of Generics in Collections
6.
Example of Generics
6.1.
Output
7.
Generic Types in Java
7.1.
Generic Classes
7.2.
Generic Interfaces
8.
Generic Methods
9.
Bounded Type Parameters
9.1.
Upper Bounds (<T extends Class>)
9.2.
Lower Bounds (<? super Class>)
10.
Wildcards in Java Generics
11.
Generics and Collections Framework
12.
Type Erasure in Java
13.
Real-World Use Cases of Generics
14.
Frequently Asked Questions
14.1.
What is a JIT compiler?
14.2.
What is the platform?
14.3.
What are the main differences between the Java platform and other platforms?
14.4.
What gives Java its 'write once and run anywhere' nature?
15.
Conclusion
Last Updated: Jun 9, 2025
Medium

Generics in Java

Author Aditya Anand
1 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Generics, which were first introduced in JDK 5, had a significant impact on Java in two ways. For starters, it gave the language a new syntactical element. Second, it resulted in changes to several of the core API's classes and methods.Some programmers were hesitant to utilise generics because they constituted such a significant change to the language. With the release of JDK 6, however, generics can no longer be overlooked. Simply simply, you'll be using generics if you're working in Java SE 6. Generics, fortunately, are simple to use and bring considerable benefits to Java programmers.

The syntax, theory, and application of generics are all covered in this article. It also demonstrates how generics provide type safety in previously challenging instances.

Generics in Java

Let’s get started!

What are Generics?

Generics refers to parameterized types at their most basic level. Parameterized types are useful because they let you to design classes, interfaces, and methods that have the kind of data they act on as a parameter. It is possible to design a single class, for example, that automatically works with several sorts of data using generics. Generic refers to a class, interface, or method that works with a parameterized type, as in generic class or generic method.

Significance of Generics

Generics can be used to create type-safe classes, interfaces, and methods that function with various types of data. Many algorithms are logically the same regardless of the type of data on which they are applied.

For example, whether a stack stores elements of type Integer, String, Object, or Thread, the mechanism that supports it is the same.

With generics, you may describe an algorithm once, regardless of the type of data, and then apply it to a wide range of data types without having to do any additional work. Generics give expressive capability to the language and significantly alter the way Java code is produced.

Also see, Duck Number in Java

Why Were Generics Introduced in Java?

Before Java 5, collections like ArrayList stored objects as type Object, which meant manual type casting was required when retrieving elements. For example:

ArrayList list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // Manual casting 

 

This approach had serious drawbacks:

  • No type safety: Collections could store any object type, leading to potential class cast issues.
  • Runtime errors: Type mismatches weren’t caught at compile time, causing failures during execution.
  • Verbose and error-prone code: Frequent manual casting made code harder to read and maintain.


Generics, introduced in Java 5, solved these problems by enabling compile-time type checking. With generics, developers can specify the type a collection should hold:

ArrayList<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0); // No casting needed 

 

Generics make code safer, cleaner, and easier to maintain by preventing type mismatches and removing the need for explicit casting.

Role of Generics in Collections

The Collections Framework is the one element of Java that has been significantly influenced by generics. The collection classes have always worked with any sort of object. Generics provide the benefit of allowing collection classes to be used with complete type safety. Generics enabled a significant improvement to an existing function in addition to introducing a powerful new language element. This is why generics are such a valuable addition to the Java language.

  • Before JDK 1.5, the Java collection framework was non-generic. It has been generic since version 1.5.
  • The new generic collection in Java allows you to have a collection with only one type of object. Because it is now type-safe, typecasting is no longer necessary at runtime.

The old non-generic example of creating a java collection.

ArrayList al=new ArrayList();

The new generic example of creating a java collection.

ArrayList<String> al=new ArrayList<String>();

The data type is specified in angular braces in the generic collection. Only specified types of objects are allowed in ArrayList now. A compile-time error occurs if you try to add another type of object.

Example of Generics

Let's start with a simple generic class example. Two classes are defined in the following programme. The first is Gen, which is a generic class, and the second is GenDemo, which is a demo that uses Gen.

// Example of a simple generic class.
// When an object of type Gen is created, type parameter T will be replaced by a real type.

class Gen<T> 
{
  public static void main(String args[]) 
  {
    T ob; //object of type T is declared
    // in the constructor pass a reference to an object of type T.
    Gen(T o) {
    ob = o;
    }
    // Return ob.
    T getob() {
    return ob;
    }
  }

 
  // print the type of T.
  void showType() {
    System.out.println("Type of T is " +
    ob.getClass().getName());
  }
}

 
// Demonstrate the generic class.
class GenDemo 
{
  public static void main(String args[]) 
  {
    // A Gen reference for Integers is created.
    
    Gen<Integer> iOb;
    
    // Create a Gen<Integer> object and assign its reference to iOb. Autoboxing is used  to encapsulate the value 48 within an Integer object.
    iOb = new Gen<Integer>(48);
    
    // Show the type of data used by iOb.
    iOb.showType();
    
    // Get the value in iOb. No cast is needed.
    int v = iOb.getob();
    System.out.println("value: " + v);
    System.out.println();
    
    // Create a Gen object for Strings.
    Gen<String> strOb = new Gen<String>("Generics Test");
   
     // Show the type of data used by strOb.
    strOb.showType();
    
    // Get the value of strOb that no cast is needed.
    String str = strOb.getob();
    System.out.println("value: " + str);
  }
}
You can also try this code with Online Java Compiler
Run Code

Output

Type of T is java.lang.Integer value: 88 
Type of T is java.lang.String value: Generics Test


Practice by yourself on java online compiler.

Generic Types in Java

Generic Classes

Generic classes in Java allow you to write type-safe, reusable code by using type parameters. Instead of writing separate classes for each data type, you can create one generic class that works for all types. For example:

class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

When using this class, you can specify the type:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");

Generic classes prevent type mismatches at compile time and eliminate the need for casting, making the code safer and more maintainable.

Generic Interfaces

Generic interfaces define type parameters that can vary based on implementation, offering flexibility and abstraction. For example:

interface Pair<K, V> {
    K getKey();
    V getValue();
}

This interface can then be implemented for different types:

class OrderedPair<K, V> implements Pair<K, V> { ... }

Generic interfaces are widely used in Java Collections, like Map<K, V>, and help create flexible APIs that work with different data types while maintaining type safety.

Generic Methods

Generic methods allow you to write reusable methods that can operate on various data types without duplication. Syntax:

public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

Example:

Integer[] intArray = {1, 2, 3};
printArray(intArray); // Works for Integer

String[] strArray = {"A", "B", "C"};
printArray(strArray); // Works for String 

Generic methods reduce redundancy and improve flexibility, as you can handle multiple data types with a single method definition.

Bounded Type Parameters

Upper Bounds (<T extends Class>)

Upper bounded generics restrict type parameters to a specific class or its subclasses. Syntax:

public <T extends Number> void display(T value) { ... }

This ensures that the method works only with Number or its subclasses (like Integer, Double). It’s useful when you need access to methods defined in the upper-bound class.

Lower Bounds (<? super Class>)

Lower bounded generics accept a specified class or any of its superclasses. Syntax:

public void addNumbers(List<? super Integer> list) { ... }

This is useful when adding elements to a collection safely. Lower bounds are commonly used when writing data into a collection, ensuring flexibility while maintaining type safety.

Wildcards in Java Generics

1. Unbounded Wildcard (?):
Accepts any type. Example:

public void printList(List<?> list) { ... }

Useful for methods that work on lists of unknown types.

2. Upper Bounded Wildcard (<? extends T>):
Accepts type T or its subclasses. Example:

List<? extends Number>

Useful when you need to read items from a list but don’t want to add to it.

3. Lower Bounded Wildcard (<? super T>):
Accepts type T or its superclasses. Example:

List<? super Integer>

Useful when you need to write to the collection but reading elements may require casting.

Wildcards improve flexibility in APIs without sacrificing type safety.

Generics and Collections Framework

Java’s Collections Framework heavily relies on generics to ensure type safety and cleaner code.
Example:

List<String> names = new ArrayList<>();
names.add("Alice");
// No casting needed when retrieving elements String name = names.get(0);

Without generics, developers had to cast elements manually:

List names = new ArrayList();
String name = (String) names.get(0); // Manual cast 

Generics also power Map<K, V> and Set<T> to ensure collections store only the specified type, reducing runtime errors and improving code readability.

Type Erasure in Java

Type erasure is the process where generic type information is removed at compile time to ensure backward compatibility with older Java versions. After compilation, generic types are replaced with raw types (Object or bounded types).
For example:

List<String> becomes List after compilation.

Because of type erasure:

  • You cannot use instanceof with generic types.
  • You cannot create arrays of generic types.
    While generics provide compile-time safety, they do not retain type information at runtime, which limits some reflective and type-checking operations.

Real-World Use Cases of Generics

Generics are widely used in enterprise-level applications to build scalable, maintainable code.

  • Collections API: Frameworks like Spring, Hibernate, and Java Collections heavily use generics for safe storage and retrieval of objects.
  • REST APIs: Response wrappers like ResponseEntity<T> in Spring Boot use generics to handle various response types efficiently.
  • Data Access Layers: Generic DAOs enable database operations for multiple entity types without rewriting code.
  • Open-Source Libraries: Libraries like Guava and Apache Commons Collections extensively leverage generics to provide flexible utilities.

Generics reduce code duplication, enforce type safety, and make large systems more maintainable and scalable.

Frequently Asked Questions

What is a JIT compiler?

JIT (Just-In-Time) compiler: It is employed in order to enhance performance. JIT compiles bits of the bytecode with similar functionality at the same time, reducing the amount of time it takes to compile. The term "compiler" refers to a translator from a Java virtual machine's (JVM) instruction set to the instruction set of a specific CPU.

What is the platform?

The hardware or software environment in which a piece of software is run is referred to as a platform. Software-based and hardware-based platforms are the two sorts of platforms. The software-based platform is provided by Java.

What are the main differences between the Java platform and other platforms?

Other platforms may be hardware platforms or software-based platforms, but Java is a software-based platform.

Other hardware platforms can only contain the hardware components, whereas Java is processed on top of them.

What gives Java its 'write once and run anywhere' nature?

The bytecode is what you're looking for. The Java compiler translates Java applications into class files (byte code), which are a language that sits between source code and machine code. This bytecode is platform agnostic, meaning it can be run on any computer.

Conclusion

In this article, we have extensively discussed Generics in Java and their implementation in java.

We have learned what is java generics, why they are so important and their significance.

We have also learnt their role in collections.

Finally, we have seen example implementations.
 

We hope that this blog has helped you enhance your knowledge regarding Generics in Java and if you would like to learn more, check out our articles on Micro services in javastatic keyword in java. Do upvote our blog to help other ninjas grow.

Recommended Readings:

Live masterclass