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