Private static members in sealed-interfaces?
Brian Goetz
brian.goetz at oracle.com
Mon Jun 23 11:49:58 UTC 2025
What I see here is an argument for private members in interfaces, but
this has nothing to do with sealed interfaces. Why is sealing relevant
to this suggestion?
On 6/22/2025 11:18 PM, Swaranga Sarma wrote:
> Traditionally Java has not allowed private static members inside of an
> interface but now with sealed interfaces, I am wondering if there is a
> case to be made. Here is a real-world use-case where I felt this was
> needed recently.
>
> I have a sealed interface hierarchy for a simple state machine
> transition result like below:
>
> ```
> sealed interface TransitionCheckResult {
> sealed interface BootstrapTransitionCheckResult
> extends TransitionCheckResult {
> record AllowedTransition(String destinationStateId, Map<String,
> String> metadata) {}
> record DisallowedTransition(Map<String, String> metadata) {}
> }
>
> sealed interface IntermediateTransitionCheckResult
> extends TransitionCheckResult {
> record AllowedTransition(String destinationStateId,
> Map<String, String> metadata,
> List<Actions> cleanupActions) {}
> record DisallowedTransition(Map<String, String> metadata) {}
> }
> }
> ```
>
> Basically the "transition check result" is broken up into a
> bootstrap transition check result for the initial state transition
> check and an "intermediary transition check result" for an
> intermediate node in the state machine each of which have their
> allowed and disallowed transitions with slightly varying attributes.
> So a sealed interface with records seemed to fit the bill here.
>
> Now for the destinationStateId, there are some constraints like length
> and Pattern (among others). I wanted to add a private static final
> Pattern variable in the TransitionCheckResult interface which I then
> use to validate the passed in destinationStateId. But unfortunately,
> this is not allowed. I think I have the following options none of
> which seem great:
> 1. Make the Pattern public; no-body else needs access to the Pattern
> field so this feels unnecessary.
> 2. Move the pattern field to inside the record class - this would work
> but I have two records that require the same validation and either I
> have to repeat myself or add the pattern to one of them and reference
> it in the other. Again, feels like I shouldn't have to introduce this
> otherwise unnecessary dependency.
> 3. Create a DestinationStateId record where I can fully encapsulate
> the validation in one place and change the AllowedTransition record
> declarations to use this record. This feels like the best approach but
> then in another part of the app, I lose the ability to do switch on
> the destinationStateId like this:
>
> ```
> static final RESOLVED_STATE_ID = new PolicyStateId("resolved);
> PolicyStateId stateId = ...;
> switch(stateId.name()) {
> case RESOLVED_STATE_ID.name() -> ... // not allowed
> ...
> }
> ```
> There are slight variations of these approaches (like using a when
> clause to address the switch problem or using a static inner Validator
> class inside the top-level sealed interface) but it feels verbose.
>
> Thoughts? Especially now with sealed interfaces and records, there
> could be more cases where this could be useful.
>
> Regards
> Swaranga
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250623/4f311a59/attachment-0001.htm>
More information about the amber-dev
mailing list