Introduction To Java Generics

Introduction To Java Generics

By - Kiran Tiwari10/11/2025

Learn the Introduction to Java Generics and understand how they enhance code reusability, type safety, and flexibility in Java programming.

 

1. Introduction

Java is a statically typed language, meaning that type checking happens at compile time. However, before Java 5, developers often used Object references to create collections or methods that could work with multiple data types. This approach was flexible but error-prone — type mismatches were caught only at runtime, not compile time.

To solve this problem, Java introduced Generics in Java 5, allowing developers to write code that is type-safe, reusable, and cleaner.

In simple words, Generics let you create classes, interfaces, and methods that can work with any type while still providing compile-time type safety.

 

2. What Are Generics in Java?

Generics mean “parameterized types.”
They allow you to create a class, interface, or method that can operate on different data types without rewriting the same code.

Example (Without Generics)

import java.util.*;

 

public class DemoWithoutGenerics {

    public static void main(String[] args) {

        List l = new ArrayList();

        l.add("Ajay");

        l.add(10); // No compile-time error

 

        for (Object ob : l) {

            String str = (String) ob; // ClassCastException at runtime

            System.out.println(str);

        }

    }

}

Problem:

  • No type checking at compile time
  • Need to cast objects manually
  • Runtime errors like ClassCastException

 

Example (With Generics)

import java.util.*;

 

public class DemoWithGenerics {

    public static void main(String[] args) {

        List<String> li = new ArrayList<>();

        li.add("Ajay");

        // li.add(10); // Compile-time error

 

        for (String s : li) {

            System.out.println(s); // No casting required

        }

    }

}

Benefits:

  • Type safety ensured
  • No explicit casting
  • Cleaner and more readable code

 

3. Why Use Generics?

Generics in Java allow you to write flexible, reusable, and type-safe code. They enable classes, interfaces, and methods to operate on different data types without sacrificing compile-time type safety.

Advantages of Generics

  1. Compile-time type safety – Catch type errors early.
  2. Code reusability – Write once, use for multiple data types.
  3. Elimination of casting – Avoid Object-type conversions.
  4. Cleaner code – Less boilerplate and fewer runtime errors.
  5. Performance – Reduces overhead from unnecessary casting.

 

4. Generic Classes

Any class that can work with any form of data is called a generic class.

Syntax

class ClassName<T> {

    

    private T dataValue; // T  define for Type parameter

 

    public ClassName(T dataValue) {

        this. dataValue = dataValue;

    }

 

    public T getData() {

        return dataValue;

    }

}

Example

public class Box<T> {

    private T valueData;

 

    public void set(T valueData) {

        this. valueData = valueData;

    }

 

    public T get() {

        return valueData;

    }

 

    public static void main(String[] args) {

        Box<Integer>  boxInt= new Box<>();

        boxInt.set(123);

        System.out.println(boxInt.get());

 

        Box<String> sBox = new Box<>();

        sBox.set("Generics ");

        System.out.println(strBox.get());

    }

}

Here, T acts as a placeholder for the type.
When you create an object, T gets replaced by a specific type (like Integer or String).

 

5. Generic Methods

generic method allows type parameters to be declared inside a method, independent of the class type.

Syntax

public <T> void display(T item) {

    System.out.println(item);

}

Example

public class DemoGenericMethod{

    public static <T> void printArray(T[] array) {

        for (T  ele : array) {

            System.out.print(ele + " ");

        }

        System.out.println();

    }

 

    public static void main(String[] args) {

        Integer[] iArr = {1, 2, 3, 4};

        String[] sArr = {"A", "B", "C"};

 

        printArray(iArr);

        printArray(sArr);

    }

}

Output:

1 2 3 4 

A B C

 

6. Bounded Type Parameters

Sometimes, you want to restrict the types that can be used as arguments in a generic class or method.

Upper Bound

<T extends ClassName>
Means T can be any subtype of ClassName (or the class itself).

Example

class         CalculatorDemo <T extends Number> {

    public double square(T num) {

        return num.doubleValue() * num.doubleValue();

    }

}

 

public class BoundedDemo {

    public static void main(String[] args) {

        CalculatorDemo<Integer> intCalc = new         CalculatorDemo <>();

        System.out.println(intCalc.square(5));

 

               CalculatorDemo <Double> doubleCalc = new         CalculatorDemo <>();

        System.out.println(doubleCalc.square(5.5));

    }

}

Lower Bound

<? super Type>
Means the unknown type must be a superclass of the given type.

 

7. Wildcards in Generics

Wildcards (?) are used when you don’t know the exact type parameter.

Types of Wildcards

TypeSyntaxMeaning
Unbounded<?>Any type
Upper bounded<? extends T>Type T or subtype of T
Lower bounded<? super T>Type T or supertype of T

Example 1: Unbounded Wildcard

public static void printList(List<?> li) {

    for (Object ob : li) {

        System.out.println(ob);

    }

}

Example 2: Upper-Bounded Wildcard

public static double sum(List<? extends Number> list) {

    double total = 0.0;

    for (Number num : list) {

        total += num.doubleValue();

    }

    return total;

}

Example 3: Lower-Bounded Wildcard

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

    list.add(10);

    list.add(20);

}

Explore Other Demanding Courses

No courses available for the selected domain.

8. Multiple Type Parameters

You can use multiple type parameters separated by commas.

Example

class Pair<K, V> {

    private K key;

    private V value;

 

    public Pair(K key, V value) {

        this.key = key;

        this.value = value;

    }

 

    public K getKey() { return key; }

    public V getValue() { return value; }

}

 

public class MultiTypeExample {

    public static void main(String[] args) {

        Pair<String, Integer> pair = new Pair<>("Age", 25);

        System.out.println(pair.getKey() + ": " + pair.getValue());

    }

}

 

9. Generic Interfaces

Interfaces can also be generic.

Example

interface Container<T> {

    void add(T item);

    T get();

}

 

class DataContainer<T> implements Container<T> {

    private T data;

 

    public void add(T item) { this.data = item; }

    public T get() { return data; }

}

 

public class GenericInterfaceExample {

    public static void main(String[] args) {

        Container<String> c = new DataContainer<>();

        c.add("Hello Generics");

        System.out.println(c.get());

    }

}

 

10. Type Inference (Diamond Operator)

From Java 7 onward, you can use the diamond operator (<>) to avoid repeating type parameters.

Example

List<String> list = new ArrayList<>(); // Type inferred

This reduces verbosity and improves readability.

 

11. Generic Constructors

Constructors can also be generic even if the class itself is not generic.

Example

class Demo {

    public <T> Demo(T data) {

        System.out.println("Value: " + data);

    }

}

 

public class GenericConstructorExample {

    public static void main(String[] args) {

        Demo obj1 = new Demo(100);

        Demo obj2 = new Demo("Java Generics");

    }

}

 

12. Limitations of Generics

Although powerful, Generics have a few limitations:

  1. Cannot use primitive types (like int, char) — must use wrapper classes.
  2. List<int> list; // Invalid
  3. List<Integer> list; //  Valid
  4. Type erasure: Type information is removed at runtime, meaning List<String> and List<Integer> are the same at runtime.
  5. Cannot create objects of type parameters
  6. T obj = new T(); //  Not allowed
  7. Cannot use static fields with type parameters.
  8. Cannot use instanceof with parameterized types.
  9. if (obj instanceof List<String>) //  Not allowed

 

13. Type Erasure (Behind the Scenes)

Java implements Generics using Type Erasure — meaning that generic type information is removed at compile time.

For example:

List<Integer> list = new ArrayList<>();

List<String> list2 = new ArrayList<>();

At runtime, both are treated as:

List list = new ArrayList();

This ensures backward compatibility with older Java code (pre-Java 5).

 

14. Real-Life Example

Let’s create a generic class that works for any type of data:

import java.util.*;

 

class Storage<T> {

    private List<T> itemList = new ArrayList<>();

 

    public void add(T itemData) {

itemList.add(itemData);

    }

 

    public void display() {

        for (T item : itemList) {

            System.out.println(item);

        }

    }

}

 

public class RealLifeGeneric {

    public static void main(String[] args) {

        Storage<String> names = new Storage<>();

        names.add("Ajay");

        names.add("Vijay");

        names.display();

 

        Storage<Integer> num = new Storage<>();

        num.add(10);

        num.add(20);

        num.display();

    }

}

This approach can easily be extended to store any data type — strings, integers, or even custom objects.

 

15. Conclusion

Generics are one of the most important and powerful features in Java.
They:

  • Provide compile-time type safety
  • Remove the need for casting
  • Enable code reusability and cleaner design

By mastering generics, you make your code more flexiblerobust, and maintainable — key qualities of professional Java development.

 

Do visit our channel to explore more:  SevenMentor

Author:- Kiran Tiwari

Get Free Consultation

Loading...

Call the Trainer and Book your free demo Class..... Call now!!!

| SevenMentor Pvt Ltd.

© Copyright 2025 | SevenMentor Pvt Ltd.

Share on FacebookShare on TwitterVisit InstagramShare on LinkedIn