
Ever heard folks rave about ADTs and wondered how that maps to plain old Java? Let’s demystify it—and put it to work with record, sealed, and pattern matching. 💪
What is an Algebraic Data Type? 🧠
An ADT is a way to model data precisely using two building blocks:
1) Product type (AND) ✖️ A type composed of multiple fields—think “A and B”. Mathematically, the number of possible values multiplies.
- Examples: a 2D point (x AND y), a money amount (value AND currency).
- In Java: a class or (better) a record.
2) Sum type (OR) ➕ A type that is one of several alternatives—think “A or B”. Mathematically, possibilities add up.
- Examples: a payment that can be Cash OR Card OR GiftCard.
- In Java: a sealed interface + record implementations (a tagged union). (Enums are a simpler sum type—great when each case has no or fixed payload.)
Explanation from Reddit
ADTs in Java: Idiomatic Patterns (Java 17–21+) ☕️
Product type → record
- Reads like data, immutable by default, compact constructor = validations/normalization.
Sum type → sealed hierarchy + record cases
Handle all variants with pattern matching for switch (Java 21, JEP 441):
A classic Sum for outcomes → Result
Great for avoiding nullable returns/exceptions for expected failures.
When should you use ADTs? 🎯
- Domain modeling: Encode business rules in the type system (states, commands, events).
- State machines & workflows: e.g., Order = Draft | Paid | Shipped | Cancelled.
- Parsing & validation: return Ok/Err instead of throwing.
- Exhaustive branching: you must handle every case—no forgotten else.
- API design: make illegal states unrepresentable.
Pros ✅
- Correctness by construction: the compiler checks you covered all cases.
- Clarity: readers see all possible shapes of a value in one place.
- Immutability by default with records → easier reasoning, thread-friendlier.
- Less null, fewer fragile strings: explicit alternatives instead.
- Refactor-friendly: adding a new variant breaks switches at compile time (good!).
Cons ⚠️
- More types upfront: small learning curve for teams new to ADTs.
- Framework friction: older libs may need tweaks for record/sealed (e.g., add Jackson modules/annotations).
- Extensibility constraints: sealed is closed on purpose (strong for safety, less open for plugin-style extension).
- Pattern matching requirements: best experience on Java 21+.
Practical tips 🛠️
- Product = “and” → record (validation in the compact ctor).
- Sum = “or” → sealed interface + record cases.
- Prefer exhaustive switch over chains of if.
- Use ADTs at bounded contexts & API boundaries—they shine where correctness matters.
- For rich error handling, model Result, Option, Either as sealed hierarchies (or use a well-known library).
Takeaways 💡
- ADTs = Product (AND) + Sum (OR) to model data precisely.
- In Java, record (product) + sealed + pattern matching (sum) give you the toolkit.
- Use them to encode domain rules, enable exhaustive handling, and reduce bugs.
- You’ll write a bit more types, but ship a lot more confidence.
#️⃣ #Java #ADTs #SealedClasses #Records #PatternMatching #FunctionalProgramming #DomainModeling #CleanCode #SoftwareDesign 🚀