Skip to main content

Generics

  • Generics in Java are a powerful feature that allows you to create classes, interfaces, and methods that operate on types specified by the programmer.

  • Generics enhance the Java programming language by enabling types (classes and methods) to be parameters when defining classes, interfaces, and methods.

  • This means you can create a single class or method that works with different types safely and without the need for casting.

Why Use Generics?​

Generics bring several key benefits to your Java programs:

  • Type Safety: Generics ensure that you can only use the specified type, preventing runtime errors.

  • Reusability: You can write a single class or method that works with different types.

  • Elimination of Casts: Generics reduce the need for casting, making the code cleaner and easier to read.

  • Compile-Time Checking: Errors related to type mismatches are caught at compile time, rather than at runtime.

Example Without Generics​

Let's start with a simple example to understand the problem that generics solve. Consider a method that prints elements. Without generics, you would need to write different versions of the same method to handle different types.

Method to Print Integers​

public void printInteger(Integer data) {
System.out.println(data);
}

Method to Print Strings​

public void printString(String data) {
System.out.println(data);
}

Problems with This Approach​

  • Code Duplication: You need to write a separate method for each type, leading to code duplication.

  • Scalability: As the number of types increases, the number of methods increases, making the code harder to manage.

Example With Generics​

Generics allow you to write a single method that works with any type. This eliminates code duplication and makes the code more scalable.

Generic Method to Print Data​

public <T> void printData(T data) {
System.out.println(data);
}

Explanation​

  • Generic Method: The method printData is defined with a type parameter T, allowing it to work with any type.

  • Type Parameter: <T> before the return type indicates that this is a generic method with a type parameter T.

  • Usage: You can call this method with different types without needing to write separate methods.

Using the Generic Method​

Here's how you can use the generic method to print different types of data:

public class Main {
public static <T> void printData(T data) {
System.out.println(data);
}

public static void main(String[] args) {
Integer intData = 123;
String stringData = "Hello, Generics!";

// Calling the generic method with an Integer
printData(intData); // Outputs: 123

// Calling the generic method with a String
printData(stringData); // Outputs: Hello, Generics!
}
}

Explanation​

  • Integer Data: printData(intData) calls the generic method with an Integer.

  • String Data: printData(stringData) calls the generic method with a String.

  • Type Safety: The compiler ensures that the types are consistent, preventing runtime errors.

Example:​

Consider a scenario where you want to create a simple Box class that can hold different types of values. Without generics, you might need to create separate classes for each data type:

// Without Generics
class IntBox {
private int value;

public void setValue(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}

class StringBox {
private String value;

public void setValue(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

With generics, you can create a single, more versatile Box class:

// With Generics
class Box<T> {
private T value;

public void setValue(T value) {
this.value = value;
}

public T getValue() {
return value;
}
}

Now, you can use the Box class to store values of any type:

public class Main{
public static void main(String args[]){
Box<Integer> intBox = new Box<>();
intBox.setValue(42);
System.out.println("Integer Value: " + intBox.getValue());

Box<String> stringBox = new Box<>();
stringBox.setValue("Hello, Generics!");
System.out.println("String Value: " + stringBox.getValue());
}
}

Explanation​

  • Integer Box: Box<Integer> intBox = new Box<>(); creates an instance of Box that can hold an Integer. intBox.setValue(42); sets the value, and intBox.getValue(); retrieves it.

  • String Box: Box<String> stringBox = new Box<>(); creates an instance of Box that can hold a String. stringBox.setValue("Hello, Generics!"); sets the value, and stringBox.getValue(); retrieves it.

  • Type Safety: The compiler ensures that the types are consistent, preventing runtime errors.

Summary​

  • Generics: Allow you to create classes, interfaces, and methods that operate on types specified by the programmer.

  • Type Safety: Ensures that you can only use the specified type, preventing runtime errors.

  • Reusability: Write a single class or method that works with different types.

  • Elimination of Casts: Reduce the need for casting, making the code cleaner and easier to read.

  • Compile-Time Checking: Errors related to type mismatches are caught at compile time, rather than at runtime.

Generics are a powerful feature that enhance the flexibility and reusability of your code.

Understanding the basics of generics is crucial for writing flexible and reusable code in Java.