Guard<T> as a @FunctionalInterface with default methods
Context
Guard<T> needs to be composable (and, or, negate) while also allowing concise definition via lambdas or method references.
Decision
Guard<T> is a @FunctionalInterface whose single abstract method is check(T value). All composition logic is implemented as default methods on the interface.
Consequences
Positive:
- The user can define a guard with a lambda:
Guard<String> notBlank = s -> Validated.valid(s). - Composition (
and,or,negate,contramap) is available without inheritance. - No state: each guard is a pure function.
Negative / tradeoffs:
default methodscannot befinal— any implementor could override the composition logic (though that would be a misuse of the API).- The interface grows in surface area as more composition operators are added.
Alternatives considered
- Abstract class
AbstractGuard<T>: allowsfinalon composition methods but prevents lambda usage and multiple inheritance. - Concrete class
Guard<T>with aFunctionfield: more rigid, not extensible as an interface.