Generic Classes
Generic Classes
A generic class is a class that is parameterised over types. Instead of hardcoding String or Integer, you declare a type parameter — a placeholder that the caller fills in. The compiler then verifies every use, eliminating the cast errors that plagued raw collections before Java 5.
Why write a generic class?
Suppose you need a simple container that holds exactly one value and can return it. Without generics you have two bad options: write a separate class for each type (StringBox, IntBox, …) or use Object and cast at every call site. Both are painful. A generic class solves both problems at once.
Declaring a generic class
Place the type-parameter list — one or more names inside angle brackets — immediately after the class name:
T is just a name; by convention single uppercase letters are used: T for type, E for element, K and V for key/value, R for return type. The compiler replaces T with whatever the caller provides.
Using a generic class
At the call site you provide the type argument in angle brackets when creating the object:
<>: Since Java 7 you can omit the type argument on the right-hand side of a declaration — the compiler infers it from the left. Always prefer new Box<>(...) over new Box<String>(...) to reduce noise.
Multiple type parameters
A class can have more than one type parameter. A classic example is a pair that holds two independent values:
Generic classes and their members
Every instance method inside a generic class can use the class-level type parameters. The type parameter acts as if it were a concrete type for the entire instance:
Static members cannot use the class type parameter
private static T cache; inside a generic class. If you need a generic static utility, declare a generic method instead (covered in the next lesson).
Raw types — what to avoid
If you use a generic class without a type argument, Java lets you — for backward compatibility — but you lose all type safety:
Never use raw types in new code. They exist only to interoperate with pre-Java-5 libraries. Modern IDEs will flag them as warnings and static analysis tools will treat them as errors.
Putting it together — a generic Result type
Here is a real-world pattern: a Result<T> that encapsulates either a success value or an error message, without throwing exceptions:
<T> declaration (a generic method) because static context cannot access the class T. The important point here is how the class-level T flows through all the instance methods naturally.
Summary
- Declare type parameters after the class name:
class Foo<T>. - Use the diamond operator
<>on the right-hand side to let the compiler infer the type. - Multiple parameters are allowed:
class Pair<A, B>. - Instance members freely use the type parameter; static members cannot.
- Avoid raw types — always provide a type argument.