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