How do I use record with generics?

Using records with generics in Java allows you to create immutable data structures while also providing the benefits of generics, such as type safety and flexibility. Since Java 16, the record feature was introduced, and it can be combined with generics like other classes. Here’s an example of how to use records with generics.

Syntax

To define a record with generics, you specify the type parameter(s) in the record declaration just like in a class declaration.

public record MyRecord<T>(T value) { }

Examples

1. Basic Generic Record

A simple record that accepts a generic type parameter:

// Define record with a generic parameter T
public record Box<T>(T content) { }

Usage:

public class Main {
    public static void main(String[] args) {
        // Create a record with a String type
        Box<String> stringBox = new Box<>("Hello, generics!");
        System.out.println(stringBox.content()); // Output: Hello, generics!

        // Create a record with an Integer type
        Box<Integer> integerBox = new Box<>(123);
        System.out.println(integerBox.content()); // Output: 123
    }
}

2. Records with Multiple Generics

You can define records with multiple type parameters, just like generic classes:

// Define a record with two generic types
public record Pair<K, V>(K key, V value) { }

Usage:

public class Main {
    public static void main(String[] args) {
        // Create a Pair with String and Integer
        Pair<String, Integer> pair = new Pair<>("Age", 30);
        System.out.println(pair.key() + ": " + pair.value()); // Output: Age: 30

        // Create a Pair with two different types
        Pair<Double, String> anotherPair = new Pair<>(3.14, "Pi");
        System.out.println(anotherPair.key() + " -> " + anotherPair.value()); // Output: 3.14 -> Pi
    }
}

3. Generics with Constraints

Generic records can include bounded type parameters to restrict the types allowed:

// Generic type T is constrained to subclasses of Number
public record NumericBox<T extends Number>(T number) { }

Usage:

public class Main {
    public static void main(String[] args) {
        // Only Number or subclasses of Number are allowed
        NumericBox<Integer> intBox = new NumericBox<>(42);
        System.out.println(intBox.number()); // Output: 42

        NumericBox<Double> doubleBox = new NumericBox<>(3.14);
        System.out.println(doubleBox.number()); // Output: 3.14

        // Compiler error: String is not a subclass of Number
        // NumericBox<String> stringBox = new NumericBox<>("Not a number");
    }
}

4. Working with Wildcards

You can use wildcards in generic records when specifying their types:

public class Main {
    public static void main(String[] args) {
        // Using a wildcard
        Box<?> anyBox = new Box<>("Wildcard content");
        System.out.println(anyBox.content()); // Output: Wildcard content

        // Using bounded wildcards
        NumericBox<? extends Number> numBox = new NumericBox<>(42);
        System.out.println(numBox.number()); // Output: 42
    }
}

Benefits of Using Generics with Records

  1. Type Safety: With generics, the compiler ensures the record is used correctly for the intended type.
  2. Reusability: You can use the same record with different data types.
  3. Immutability: Records’ inherent immutability, coupled with generics, allows you to encapsulate type-safe, immutable data structures.

This approach works seamlessly with the other features of records, such as pattern matching and compact constructors. Let me know if you’d like more advanced scenarios!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.