Menu
Coddy logo textTech

Introduction to Generics

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 53 of 87.

Imagine you need to create a class that stores a value and retrieves it later. Without generics, you'd have to write separate classes for each type—one for Integer, one for String, one for Double, and so on. This leads to code duplication and maintenance headaches.

Generics solve this problem by allowing you to write classes and methods that work with any type, while still maintaining type safety. You define a placeholder (called a type parameter) that gets replaced with an actual type when you use the class:

// Without generics - uses Object, requires casting
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Manual casting needed

// With generics - type-safe, no casting
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // No casting needed

The angle brackets <T> denote a type parameter. When you write List<String>, you're telling the compiler that this list will only contain strings. If you try to add an integer, the compiler catches the error immediately rather than failing at runtime.

Generics provide two key benefits: they eliminate the need for explicit casting, and they catch type errors at compile time instead of runtime. This makes your code both safer and cleaner. In the following lessons, we'll explore how to create your own generic classes and methods.

challenge icon

Challenge

Easy

Let's build a simple storage system that demonstrates the power of generics by comparing the old way (using Object with casting) versus the new way (using generics with type safety).

You'll create three files to see the difference firsthand:

  • OldBox.java: Create a class that stores values the pre-generics way. Your OldBox should have a private field content of type Object. Include a constructor that accepts an Object and stores it, and a getContent() method that returns the Object. This approach works but requires casting when retrieving values.
  • GenericBox.java: Now create the modern, type-safe version using generics. Your GenericBox should use a type parameter T in its declaration. It needs a private field content of type T, a constructor that accepts a T value, and a getContent() method that returns type T. No casting needed when using this class!
  • Main.java: Demonstrate both approaches side by side. You'll receive two inputs: a word (String) and a number (integer).

    First, use the old approach: create an OldBox storing the word, then retrieve it with a cast to String and print: OldBox (with cast): [value]

    Next, use the generic approach: create a GenericBox<String> storing the same word, retrieve it without casting, and print: GenericBox (no cast): [value]

    Then show generics working with integers: create a GenericBox<Integer> storing the number, retrieve it, and print: GenericBox Integer: [value]

    Finally, print a blank line followed by: Type safety: Generics catch errors at compile time!

You will receive two inputs in order: a word (String) and a number (integer as String, which you'll parse).

Notice the key difference: with OldBox, you must cast (String) box.getContent() and hope you got the type right. With GenericBox, the compiler knows the type and no casting is needed. This is the core benefit of generics—type safety without the casting hassle!

Cheat sheet

Generics allow you to write classes and methods that work with any type while maintaining type safety. They use type parameters (placeholders) that get replaced with actual types when the class is used.

Without generics (using Object):

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

With generics (type-safe):

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

Creating a generic class:

public class GenericBox<T> {
    private T content;
    
    public GenericBox(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

Using a generic class:

GenericBox<String> stringBox = new GenericBox<String>("Hello");
String value = stringBox.getContent();  // No casting

GenericBox<Integer> intBox = new GenericBox<Integer>(42);
Integer number = intBox.getContent();  // Type-safe

The angle brackets <T> denote a type parameter. Benefits include: no explicit casting needed and type errors caught at compile time instead of runtime.

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: Create an OldBox storing the word
        // Retrieve with cast to String and print: "OldBox (with cast): [value]"
        
        // TODO: Create a GenericBox<String> storing the word
        // Retrieve without casting and print: "GenericBox (no cast): [value]"
        
        // TODO: Create a GenericBox<Integer> storing the number
        // Retrieve and print: "GenericBox Integer: [value]"
        
        // TODO: Print a blank line, then print:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming