Java Generics: Type-Safe and Flexible Programming

What are Java Generics?

Generics in Java provide a way to create classes, interfaces, and methods that can work with different data types while ensuring type safety.

Java Generics

Why Generics was Introduced in Java 5?

Arrays in Java have always been type safe. An String array can’t accept Integers (or ints) or anything other than Strings.

String strArray[] = new String[5];

We can put only String into strArray like below:

strArray[0] = “ABC”; // will compile
strArray[0]=6; // throws compilation error: Type mismatch

Before Java 5 there was no syntax for declaring a type safe collection.

List myList = new ArrayList();
There was no syntax that let you specify that myList will take Strings and only Strings.

Generics was added in Java 5 to provide type safety in collections.

Let us know the other reason for the incorporation of Generics into Java 5.

Let us execute the below non Generic code:

List list = new ArrayList(); // Accepts any Type
list.add("String");
list.add(new Integer(10));

for(int i=0; i<list.size(); i++){
String s = (String) list.get(i);
}

The above will throw: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

With Generics , you will get the compilation error when you try to add an Integer into a String ArrayList.

List<String> list = new ArrayList<String>();
list.add("String");
list.add(new Integer (10));

With Generics, we  don’t need to do the casting while iterating over it.

List<String> list = new ArrayList<String>();
list.add("String");
list.add("String1");
}
for(int i=0; i<list.size(); i++){
String s = list.get(i);
}

So , Generics was also added to resolve type casting problems.

Code Snippet: Java Generics

Here’s a sample Java code that demonstrates the use of generics in a generic class and method:

public class GenericExample<T> {
    private T data;

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

    public T getData() {
        return data;
    }

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

    public static void main(String[] args) {
        // Create an instance of GenericExample with Integer type
        GenericExample<Integer> integerExample = new GenericExample<>(42);
        int intValue = integerExample.getData();
        System.out.println("Integer value: " + intValue);

        // Create an instance of GenericExample with String type
        GenericExample<String> stringExample = new GenericExample<>("Hello, Generics!");
        String strValue = stringExample.getData();
        System.out.println("String value: " + strValue);

        // Using the generic method to print an array of integers
        Integer[] intArray = {1, 2, 3, 4, 5};
        printArray(intArray);

        // Using the generic method to print an array of strings
        String[] strArray = {"Apple", "Banana", "Orange"};
        printArray(strArray);
    }
}

In the above code, we have a generic class GenericExample with a type parameter T. The constructor takes a parameter of type T, and the getData() method returns the data of type T. We create instances of GenericExample with different types (Integer and String) and retrieve their data.

Additionally, there is a generic method printArray that accepts an array of any type E and prints its elements using a for-each loop.

When we run the main method, it demonstrates the usage of the generic class and the generic method. It creates instances of GenericExample with different types and prints their data. Then, it uses the generic method printArray to print arrays of integers and strings.

Usage of Java Generics

Generic Classes

You can create generic classes by declaring one or more type parameters using angle brackets (<>). These type parameters can be used as placeholders for specific types. For example, ArrayList is a generic class that represents a dynamic array that can hold elements of any type T.

public class Box<T> {
    private T item;

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

    public T getItem() {
        return item;
    }
}

Type Parameters

Type parameters act as placeholders for actual types. They are specified when creating an instance of a generic class or invoking a generic method. For example, ArrayList creates an instance of ArrayList with the type parameter Integer, allowing it to hold only Integer objects.

public <T extends Number> double sum(T num1, T num2) {
    return num1.doubleValue() + num2.doubleValue();
}

Generic Interfaces

Similar to generic classes, you can create generic interfaces. These interfaces can be implemented by classes that provide specific types for the type parameters. For example, the Comparable interface is a generic interface that allows objects to be compared using a specific type T.

public interface List<T> {
    void add(T item);
    T get(int index);
}

Generic Methods

You can define generic methods that have their own type parameters. These methods can be used to work with different types dynamically. For example, a generic method printArray can print elements of an array of any type T.

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

Wildcard Types in Java Generics

Wildcard types, denoted by ?, are used when you want to work with unknown types in a generic context. They provide flexibility when you don’t need to specify a specific type. For example, List represents a list of unknown type.

import java.util.List;

public class WildcardExample {

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

    public static void main(String[] args) {
        List<Integer> integerList = List.of(1, 2, 3, 4, 5);
        List<String> stringList = List.of("Apple", "Banana", "Orange");

        System.out.println("Printing integerList:");
        printList(integerList);

        System.out.println("Printing stringList:");
        printList(stringList);
    }
}

In the above code, we have a method printList that takes a List with a wildcard type parameter ?. The wildcard type ? represents an unknown type, allowing the method to accept a list of any type.

By using generics, you can create reusable components that are flexible and type-safe. Here are some key concepts related to Java Generics:

Conclusion: Java Generics

Generics in Java provide benefits such as enhanced code reusability, type safety, and compile-time type checking. They enable you to write more generic and flexible code that can handle different data types without sacrificing type safety.

Good Reads:

Generics and Polymorphism

Type Eraser in Java Generics

Leave a Reply

Your email address will not be published. Required fields are marked *