javaadvanced

Java Generics — Bounded Types and Wildcards

Master Java generics: bounded types, wildcards, generic methods, and type-safe collection utilities.

java
import java.util.*;
import java.util.function.*;

public class GenericsDemo {

    // Generic method — bounded type
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) >= 0 ? a : b;
    }

    // Multiple bounds
    public static <T extends Comparable<T> & Serializable> List<T> sort(List<T> list) {
        var sorted = new ArrayList<>(list);
        Collections.sort(sorted);
        return sorted;
    }

    // Generic class
    static class Pair<A, B> {
        private final A first;
        private final B second;

        Pair(A first, B second) {
            this.first = first;
            this.second = second;
        }

        public <C> Pair<C, B> mapFirst(Function<A, C> fn) {
            return new Pair<>(fn.apply(first), second);
        }

        A first() { return first; }
        B second() { return second; }
    }

    // Wildcards
    // Upper bound: ? extends Number — read-only (producer)
    static double sum(List<? extends Number> numbers) {
        return numbers.stream()
            .mapToDouble(Number::doubleValue)
            .sum();
    }

    // Lower bound: ? super Integer — write-only (consumer)
    static void addNumbers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
    }

    // Generic type-safe builder
    static class Result<T> {
        private final T value;
        private final String error;

        private Result(T value, String error) {
            this.value = value;
            this.error = error;
        }

        static <T> Result<T> ok(T value) { return new Result<>(value, null); }
        static <T> Result<T> err(String msg) { return new Result<>(null, msg); }

        public <U> Result<U> map(Function<T, U> fn) {
            return error != null ? err(error) : ok(fn.apply(value));
        }

        boolean isOk() { return error == null; }
        T get() { return value; }
    }

    public static void main(String[] args) {
        System.out.println(max(3, 7));       // 7
        System.out.println(max("apple", "banana")); // banana

        System.out.println(sum(List.of(1, 2.5, 3L))); // 6.5

        var result = Result.ok(42)
            .map(n -> n * 2)
            .map(Object::toString);
        System.out.println(result.get()); // 84
    }
}

Use Cases

  • Type-safe generic utility methods and classes
  • Building Result/Either types for error handling
  • Collection utilities with bounded generics

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.