Generics in Java

Generics in Java

Generics replace runtime failure with compile time guarantees. Before Java 5, casts hid defects until production execution.

Do Not Use Raw Types

Raw types disable generic checking.

Bad:
List list = new ArrayList();
list.add("Hello");
list.add(123);

String s = (String) list.get(1); // ClassCastException        
Generic form:
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // compile error        

Benefits:

  • Errors move from runtime to compile time
  • APIs express intent
  • Refactoring becomes safe

Raw types exist for backward compatibility only.

Example

Trade caches storing Trade objects inside raw List allow accidental insertion of String audit messages. Pricing logic casts blindly and fails during valuation windows. Generic collections stop this defect before deployment.


Prefer Lists to Arrays

Arrays and generics differ in two fundamental ways.

1) Covariance vs Invariance

Arrays are covariant.

Object[] objects = new Long[1];
objects[0] = "FAIL"; // ArrayStoreException        

Compiles, fails at runtime.

Generics are invariant.

List<Object> objects = new ArrayList<Long>(); // compile error        

Fails at compile time, not in production.

2) Reification vs Erasure

Arrays keep element type at runtime. Java checks every store.

Generics erase type info at runtime. List<String> and List<Integer> both appear as List.

Mixing both models leads to unsafe constructs.

Bad idea:
List<String>[] table = new List<String>[10];        
Correct approach:
List<List<String>> table = new ArrayList<>();        

Example

Portfolio matrices stored as arrays accept wrong instrument types silently until valuation time. Using List<List<Position>> moves detection to compilation, not market open.


Use Bounded Wildcards for Flexible APIs

Generic APIs become rigid without wildcards.

Problem:
public void pushAll(Iterable<E> src)        
Usage:
Stack<Number> stack = new Stack<>();
Iterable<Integer> ints = ...;

stack.pushAll(ints); // compile error        

Iterable<Integer> is not Iterable<Number>.

Fix:
public void pushAll(Iterable<? extends E> src)        

Now subtypes work.

PECS Rule

Producer Extends, Consumer Super.

  • Producer (extends) Source provides values.
  • Consumer (super) Destination accepts values.

This keeps APIs open without sacrificing safety.

Example

Order routers accept batches of EquityOrder into a Collection<Order>. Without wildcards, teams duplicate pipelines. With PECS, routing logic supports every subtype cleanly.


Design Rules

  • Never write new raw types
  • Prefer List over arrays for generics
  • Avoid generic arrays
  • Use bounded wildcards in APIs
  • Apply PECS for source and destination parameters


Takeaway

Generics enforce contracts at compile time. They shift failure left, away from production windows.

To view or add a comment, sign in

More articles by Ankur Mistry

  • Lambdas, Method References, Streams

    Prefer Lambdas to Anonymous Classes Before Java 8: With lambdas: Shorter. Clearer.

  • Prefer Interfaces to Abstract Classes

    Java supports single inheritance. Extending an abstract class consumes the only superclass slot.

  • Inheritance vs Composition in Java

    Object reuse looks simple: extend a class and gain behavior. In production systems, inheritance creates long term risk…

  • Control Access to Protect Design

    Encapsulation drives decoupling. Once access spreads, refactoring cost rises across teams and systems.

  • Methods Common to All Objects

    Obey the General Contract When Overriding equals Overriding equals looks simple: check whether two objects match. In…

  • Avoid Finalizers and Cleaners

    Never rely on object finalization for resource management. In C++, destructors run at deterministic points.

  • Avoid Creating Unnecessary Objects

    This item sounds simple, yet performance loss often starts here. Classic Mistake: Redundant Allocation Better form:…

  • Prefer Dependency Injection to Hardwiring Resources

    A design rule many engineers follow instinctively, yet often violate under time pressure. Problem: Hardwired…

  • Enforce Noninstantiability

    Some classes exist only as containers for static methods and constants. Examples from the JDK: These classes represent…

  • Singleton Instance

    Enforcing a Single Instance with the Singleton Property After covering factories and builders, object creation control…

Explore content categories