Why generics & generic classes
Type parameters give you compile-time type safety and remove casts — the TwoTuple<A,B> the Greenhouse project uses.
The problem generics solve
Before generics (Java 1.5), a container held Object, so the compiler could not tell *what* you put in it. You paid for that on the way out, with a cast that the compiler trusted blindly:
List list = new ArrayList(); // raw type — holds Object
list.add("hello");
list.add(42); // compiles! nothing stops you
String s = (String) list.get(1); // compiles, then THROWS ClassCastException at runtime
The bug (42 is not a String) is invisible until the program runs. Generics move that check to compile time. You tell the compiler the element type once, and it enforces it everywhere:
List<String> list = new ArrayList<>();
list.add("hello");
list.add(42); // COMPILE ERROR — caught immediately
String s = list.get(0); // no cast needed — return type is already String
Two wins: type safety (the wrong type is rejected at compile time) and no casts (get returns the parameter type directly). The <> on the right is the *diamond operator* — the compiler infers the type argument from the left-hand declaration.
Writing a generic class
You parameterise a class by listing type parameters in angle brackets after the class name. Inside the class, those names stand in for whatever concrete type a caller supplies. Here is the TwoTuple<A,B> the Greenhouse control system uses to return a pair of values:
public class TwoTuple<A, B> {
public final A first;
public final B second;
public TwoTuple(A a, B b) {
first = a;
second = b;
}
@Override
public String toString() {
return "(" + first + ", " + second + ")";
}
}
A and B are not real types — they are placeholders. When you write new TwoTuple<String, Integer>("id", 7), the compiler treats first as a String and second as an Integer for that object. A different call, new TwoTuple<Event, Long>(e, time), reuses the *same class* with different types.
Naming convention. Single uppercase letters: T (type), E (element), K/V (key/value), A/B (when you need two unrelated types). Multiple parameters are comma-separated.
Why not just use Object?
You *could* write a class with Object first; Object second; — but then every caller must cast on the way out and the compiler cannot stop them mixing types. Generics give you exactly one declaration site and many type-checked use sites.