Return to site

What is ADT? Algebraic Data Types in Java, Explained 🔎

Section image

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 🚀