// GenericsDemo.java — generic classes, generic methods and bounded types.
import java.util.ArrayList;
import java.util.List;

public class GenericsDemo {

    // A generic class: T is decided by whoever uses it
    static class Box<T> {
        private final T value;
        Box(T value) { this.value = value; }
        T get() { return value; }
        boolean isPresent() { return value != null; }
    }

    // A generic pair with two type parameters
    record Pair<A, B>(A first, B second) { }

    // A generic method: works for any type T
    static <T> T firstOrDefault(List<T> list, T fallback) {
        return list.isEmpty() ? fallback : list.get(0);
    }

    // Bounded type: only numbers allowed
    static <T extends Number> double sum(List<T> numbers) {
        double total = 0;
        for (T n : numbers) {
            total += n.doubleValue();
        }
        return total;
    }

    // Wildcard: read from a list of any subtype of Number
    static void printAll(List<? extends Number> numbers) {
        numbers.forEach(n -> System.out.print(n + " "));
        System.out.println();
    }

    public static void main(String[] args) {
        Box<String> name = new Box<>("Acme");
        Box<Integer> qty = new Box<>(24);
        System.out.println(name.get().toUpperCase() + " " + (qty.get() + 1));

        Pair<String, Double> line = new Pair<>("AX-200", 8.50);
        System.out.println(line.first() + " costs " + line.second());

        System.out.println(firstOrDefault(new ArrayList<String>(), "none"));   // none
        System.out.println(sum(List.of(1, 2, 3.5)));                           // 6.5
        printAll(List.of(1, 2, 3));
    }
}
