Private static members in sealed-interfaces?

Swaranga Sarma sarma.swaranga at gmail.com
Mon Jun 23 03:18:33 UTC 2025


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/20250622/bdc932a4/attachment.htm>


More information about the amber-dev mailing list