Generics, bounded types, and wildcards
Type-safe containers, PECS (producer-extends, consumer-super), and erasure's sharp edges.
Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.
Why generics
Before Java 5, collections held Object and every read required a cast — a runtime ClassCastException waiting to happen. Generics push type safety to the compiler and remove the casts.
Generic methods and classes
Declare a type parameter with <T> before the return type on a method, or after the class name on a class. Multiple parameters are comma-separated: <K, V>. By convention, E = element, K = key, V = value, T = type, R = result.
Bounded type parameters
<T extends Comparable<T>> restricts T to types that implement Comparable. This unlocks x.compareTo(y) inside the method body. You can chain bounds with &: <T extends Number & Comparable<T>>.
Wildcards — ? extends and ? super
List<? extends Number> is a read-only list of some subtype of Number — you can read Number out but can't add anything (except null). List<? super Integer> accepts Integers as input but reads out as Object. Remember PECS: Producer Extends, Consumer Super.
Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.
Type erasure
At runtime, List<String> and List<Integer> are both just List. Generic type information is erased. Consequences: you can't do new T[10], you can't use instanceof List<String>, and overloaded methods that differ only in generic parameters won't compile. Pass a Class<T> token when you need the runtime type.
Common pitfalls
Mixing raw types (List without <...>) with generic code disables all type checks in that scope and produces warnings — never do it in new code. Arrays and generics don't mix: new List<String>[10] is illegal. Use List<List<String>> or a typed array of raw List with an unchecked cast, and be honest about why.