Java Cheatsheet
Generics
Use this Java reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Why Generics
Type-safe, reusable code without casting. Errors caught at compile time, not runtime.
// Without generics (pre-Java 5) List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); // cast required, ClassCastException risk // With generics List<String> list = new ArrayList<>(); list.add("hello"); String s = list.get(0); // no cast needed, compile-time safety
Generic Classes
public class Box<T> { private T value; public Box(T value) { this.value = value; } public T getValue() { return value; } public void setValue(T value) { this.value = value; } @Override public String toString() { return "Box[" + value + "]"; } } Box<Integer> intBox = new Box<>(42); Box<String> strBox = new Box<>("hello"); int n = intBox.getValue(); // no cast // Multiple type parameters public class Pair<A, B> { public final A first; public final B second; public Pair(A first, B second) { this.first = first; this.second = second; } } Pair<String, Integer> p = new Pair<>("age", 30);
Generic Methods
// Type parameter declared before return type public static <T> T identity(T value) { return value; } public static <T extends Comparable<T>> T max(T a, T b) { return a.compareTo(b) >= 0 ? a : b; } // Multiple type parameters public static <K, V> Map<V, K> invertMap(Map<K, V> map) { Map<V, K> result = new HashMap<>(); map.forEach((k, v) -> result.put(v, k)); return result; } // Usage — type inferred from arguments String s = identity("hello"); int largest = max(3, 7);
Generic Interfaces
public interface Repository<T, ID> { T findById(ID id); List<T> findAll(); void save(T entity); void delete(ID id); } public class UserRepository implements Repository<User, Long> { @Override public User findById(Long id) { ... } @Override public List<User> findAll() { ... } @Override public void save(User user) { ... } @Override public void delete(Long id) { ... } }
Bounded Type Parameters
// Upper bound — T must be Number or a subclass public static <T extends Number> double sum(List<T> list) { return list.stream().mapToDouble(Number::doubleValue).sum(); } sum(List.of(1, 2, 3)); // Integer extends Number — OK sum(List.of(1.5, 2.5)); // Double extends Number — OK // sum(List.of("a","b")); // compile error // Multiple bounds — T must implement both interfaces public static <T extends Comparable<T> & Cloneable> T clampedMax(T a, T b) { return a.compareTo(b) >= 0 ? a : b; } // Lower bound (wildcards only — see below)
Wildcards
// ? — unknown type; read-only in most contexts List<?> unknown = new ArrayList<String>(); Object obj = unknown.get(0); // can only get as Object // unknown.add("x"); // compile error — type unknown // Upper bounded wildcard: ? extends T (producer — read from) public static double sumList(List<? extends Number> list) { return list.stream().mapToDouble(Number::doubleValue).sum(); } sumList(List.of(1, 2, 3)); // OK sumList(List.of(1.5, 2.5)); // OK // Lower bounded wildcard: ? super T (consumer — write to) public static void addNumbers(List<? super Integer> list) { list.add(1); list.add(2); list.add(3); } List<Number> nums = new ArrayList<>(); List<Object> objs = new ArrayList<>(); addNumbers(nums); // OK — Number is a supertype of Integer addNumbers(objs); // OK // addNumbers(List<Double>) — compile error
PECS Principle
Producer Extends, Consumer Super.
// Copy elements from src to dst public static <T> void copy(List<? super T> dst, List<? extends T> src) { for (T item : src) dst.add(item); } // Unbounded wildcard — when only Object methods are used public static void printList(List<?> list) { for (Object o : list) System.out.println(o); }
Type Erasure
Generic type information is erased at runtime; List<String> and List<Integer> are both List at runtime.
List<String> strings = new ArrayList<>(); List<Integer> ints = new ArrayList<>(); strings.getClass() == ints.getClass() // true — both are ArrayList // Cannot do: // new T() // compile error // new T[] // compile error // instanceof List<String> // compile error // T.class // compile error // Workaround — pass Class<T> as a parameter public static <T> T create(Class<T> cls) throws Exception { return cls.getDeclaredConstructor().newInstance(); }
Generic Collections Patterns
// Stack using Deque Deque<Integer> stack = new ArrayDeque<>(); stack.push(1); stack.push(2); int top = stack.pop(); // Generic utility: swap elements in a list public static <T> void swap(List<T> list, int i, int j) { T tmp = list.get(i); list.set(i, list.get(j)); list.set(j, tmp); } // Generic singleton factory @SuppressWarnings("unchecked") public static <T> Set<T> emptySet() { return (Set<T>) Collections.EMPTY_SET; }
Comparable and Comparator Generics
// Class T that can compare to itself public class Temperature implements Comparable<Temperature> { private final double celsius; Temperature(double celsius) { this.celsius = celsius; } @Override public int compareTo(Temperature other) { return Double.compare(this.celsius, other.celsius); } } // Generic max with Comparable bound public static <T extends Comparable<T>> T max(List<T> list) { return list.stream().max(Comparator.naturalOrder()).orElseThrow(); } // Comparator chain Comparator<Person> cmp = Comparator .comparingInt(Person::getAge) .thenComparing(Person::getName) .reversed();
Generic Type Tokens
// Passing Class<T> to retain type at runtime (common in JSON/serialization libs) public <T> T readJson(String json, Class<T> type) { return objectMapper.readValue(json, type); } User user = readJson(json, User.class);
Recursive Generic Bounds
// Self-referential bound — T compares to itself (enables chaining) public class Builder<T extends Builder<T>> { @SuppressWarnings("unchecked") public T withName(String name) { this.name = name; return (T) this; } }
Common Generic Gotchas
// Cannot create generic arrays // T[] arr = new T[10]; // compile error Object[] arr = new Object[10]; // workaround // Cannot use instanceof with parameterized types // if (list instanceof List<String>) // compile error if (list instanceof List<?>) // OK // Static fields/methods cannot use class type parameters class Foo<T> { // static T value; // ERROR — T is per-instance static <T> void bar() {} // OK — method-level T is separate } // Unchecked cast warning when mixing generics and raw types List raw = new ArrayList(); List<String> checked = (List<String>) raw; // @SuppressWarnings("unchecked") // Generic exception cannot be caught (but can be thrown via type param) // catch (T e) { } // compile error