Menu
Coddy logo textTech

Generic Classes

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

Now that you understand why generics are useful, let's create your own generic class. A generic class declares one or more type parameters in angle brackets after the class name, which can then be used throughout the class.

class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

The T is a type parameter—a placeholder that gets replaced with an actual type when you create an instance. You can use any name, but single uppercase letters are conventional: T for Type, E for Element, K for Key, V for Value.

When instantiating a generic class, you specify the actual type in angle brackets:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // No casting needed

Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();

Notice the <> (diamond operator) on the right side—Java infers the type from the declaration. You can also use multiple type parameters for classes that need to work with more than one type:

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; }
}

Pair<String, Integer> pair = new Pair<>("age", 25);
challenge icon

Challenge

Easy

Let's build a gift registry system that showcases the power of generic classes. You'll create a flexible storage system that can hold any type of gift item, plus a registry that pairs gift names with their details using multiple type parameters.

You'll organize your code across three files:

  • GiftBox.java: Create a generic class that can wrap any type of gift. Your GiftBox<T> should have a private field item of type T and a private field wrapped (boolean) that starts as false. Include a constructor that accepts the item, a getItem() method that returns the item, a wrap() method that sets wrapped to true, and an isWrapped() method that returns the wrapped status. Add a getStatus() method that returns either [item] (wrapped) or [item] (unwrapped) depending on the wrapped state.
  • Registry.java: Create a generic class with two type parameters that pairs a recipient with their gift. Your Registry<K, V> should have private fields recipient of type K and gift of type V. Include a constructor that accepts both values, getter methods getRecipient() and getGift(), and a getEntry() method that returns: [recipient] -> [gift]
  • Main.java: Bring your gift registry to life! You'll receive three inputs: a recipient name (String), a gift description (String), and a gift value (integer representing dollars).

    First, create a GiftBox<String> containing the gift description. Wrap it using the wrap() method, then print its status.

    Next, create a GiftBox<Integer> containing the gift value (representing a gift card amount). Don't wrap this one—print its status to show it's unwrapped.

    Finally, create a Registry<String, String> pairing the recipient name with the gift description, and print the registry entry.

You will receive three inputs in order: recipient name, gift description, and gift value (as an integer).

Notice how the same GiftBox class works seamlessly with both String and Integer types—that's the beauty of generics! And the Registry class demonstrates how multiple type parameters let you create flexible pairings between any two types.

Cheat sheet

A generic class declares one or more type parameters in angle brackets after the class name, which can be used throughout the class:

class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

The T is a type parameter—a placeholder replaced with an actual type when creating an instance. Common conventions use single uppercase letters: T for Type, E for Element, K for Key, V for Value.

When instantiating a generic class, specify the actual type in angle brackets:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // No casting needed

Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();

The <> (diamond operator) allows Java to infer the type from the declaration.

Use multiple type parameters for classes that work with more than one type:

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; }
}

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

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Read inputs
        String recipientName = scanner.nextLine();
        String giftDescription = scanner.nextLine();
        int giftValue = scanner.nextInt();
        
        // TODO: Create a GiftBox<String> containing the gift description
        // TODO: Wrap it using the wrap() method
        // TODO: Print its status using getStatus()
        
        // TODO: Create a GiftBox<Integer> containing the gift value
        // TODO: Don't wrap this one - print its status (should show unwrapped)
        
        // TODO: Create a Registry<String, String> pairing recipient with gift description
        // TODO: Print the registry entry using getEntry()
    }
}
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