Option<T> as a custom type instead of java.util.Optional
Context
Java provides java.util.Optional<T> as a standard absent-value container. dmx-fun must decide whether to use it directly or provide a custom Option<T> type.
Decision
Provide a custom Option<T> sealed interface with Some<T> and None<T> record variants, rather than wrapping or extending java.util.Optional.
Consequences
Positive:
- Exhaustive pattern matching.
Optionalis afinalclass;Optionis asealed interface. The compiler enforces that bothSomeandNonebranches are handled in a switch expression — no wildcard arm needed, unlikeOptional.isPresent()checks. - Type-graph integration.
Option<T>participates in the library’s type graph with first-class conversion methods:toResult(),toTry(),toEither(). The reverse conversions are symmetric:Option.fromOptional(),Option.fromResult(),Option.fromTry(). - Consistent pipeline API.
map,flatMap,filter,zip,fold, andsequenceall returnOption<T>, keeping pipelines within the library’s type system. - Singleton absence value.
Option.none()returns a pre-allocated, cast-safe singleton (None.INSTANCE), avoiding repeated allocations of the absent case.
Negative / tradeoffs:
- Users must convert at JDK API boundaries:
Option.fromOptional(opt)/option.toOptional(). - Two absent-value types in the same codebase can be confusing without clear conventions; the rule is: use
Option<T>within library and domain code, convert toOptionalonly at JDK or framework API boundaries.
Alternatives considered
- Use
java.util.Optionaldirectly: no custom type needed, butOptionalis afinalclass — not a sealed interface — so exhaustive compile-time matching is impossible. It also has no integration withResult,Try, orEither, and itsget()method throwsNoSuchElementExceptionwithout statically enforcing a priorisPresent()check. - Wrap
OptionalinOption<T>: adds indirection without benefit; the innerOptionalis redundant once the sealed interface is in place.