Generics in Java

What are Generics?

Generics in Java allow developers to write type-safe and reusable code. Introduced in Java 5, generics enable you to define classes, interfaces, and methods with type parameters.

Without generics, we would have to rely on casting and lose compile-time safety. Generics solve this problem elegantly.

Why Use Generics?

  • Type Safety: Detect errors at compile-time instead of runtime.
  • Elimination of Casting: No need for explicit type casting when retrieving elements.
  • Code Reusability: Write a single method or class to work with different types.

Syntax of Generics

The basic syntax of generics involves the use of type parameters denoted by angle brackets <>.

public class Box<T> {
    private T item;

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

    public T getItem() {
        return item;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setItem("Hello Generics");
        System.out.println(stringBox.getItem()); // Outputs: Hello Generics

        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        System.out.println(intBox.getItem()); // Outputs: 123
    }
}
            

Here, T is a type parameter. It can represent any reference type.

Type Parameters

Java conventions for type parameters are as follows:

  • T - Type
  • E - Element (used in collections like Lists)
  • K - Key (used in maps)
  • V - Value (used in maps)
  • N - Number

Generic Methods

You can also create methods with generics:

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

    public static void main(String[] args) {
        String[] strings = {"Apple", "Banana", "Cherry"};
        Integer[] numbers = {1, 2, 3};

        printArray(strings); // Works with Strings
        printArray(numbers); // Works with Integers
    }
}
            

Wildcards in Generics

Wildcards allow flexibility with generics. There are three types of wildcards:

  • ? - Unbounded wildcard
  • ? extends X - Upper-bounded wildcard
  • ? super X - Lower-bounded wildcard
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

public static void main(String[] args) {
    List<Integer> intList = Arrays.asList(1, 2, 3);
    List<String> strList = Arrays.asList("A", "B", "C");

    printList(intList); // Accepts any list
    printList(strList); // Accepts any list
}
            

Bounded Type Parameters

Sometimes, you need to restrict type parameters to a specific class hierarchy:

public static <T extends Number> void printDoubleValue(T number) {
    System.out.println(number.doubleValue());
}

public static void main(String[] args) {
    printDoubleValue(10);       // Works with Integer
    printDoubleValue(10.5);     // Works with Double
}
            

Generics and Collections

Generics are extensively used with Java Collections. For example:

List<String> list = new ArrayList<>();
list.add("Java Generics");
list.add("Collections");
for (String item : list) {
    System.out.println(item);
}
            

Conclusion

Generics in Java provide a powerful way to ensure type safety, improve code readability, and eliminate unnecessary casting. By understanding type parameters, wildcards, and their use in collections, developers can write clean, reusable, and robust code.

Post a Comment

0 Comments