Private static members in sealed-interfaces?

Brian Goetz brian.goetz at oracle.com
Mon Jun 23 12:19:46 UTC 2025


Interfaces are allowed to have (public) fields today, but they are 
always static.  So we're just talking about access control.

My point is that I think the anomaly you are really raising is that 
access control for interface members is not uniform, but without a clear 
justification other than history.  Private _methods_ were introduced 
into interfaces in Java 8 (to go with default methods), but there's no 
good reason why private fields (interfaces already have fields, they are 
static) or private nested classes did not get the same treatment.  This 
is a topic for potential cleanup at some point.

But, if I understand your clarification, you are saying: "sealed classes 
let me treat all the implementations of a type as being a single big 
implementation, and sometimes these might want to share state, and the 
sealed class is a sensible place to put state shared across all 
implementations"?  (And then taking advantage of the nestmate status of 
co-declared classes, so that "private" isn't really private to the 
interface, but private to the source file.)

On 6/23/2025 8:04 AM, Swaranga Sarma wrote:
> Hi Brian, I am afraid I am not seeing how I am making a case for 
> private members in interfaces. That would be introducing state into an 
> interface and seems pretty large change to the language spec.
>
> As for the suggestion, yes, the post does make it seem like it is 
> specific to sealed interfaces but wasn’t intended as such. I was using 
> it as a real example from my work which uses a sealed hierarchy. 
> Although now that I think about it, for regular interfaces I don’t see 
> how it would be helpful if these fields are private to the interface.
>
> I have felt its need more in sealed interfaces where I want to share 
> common static fields and methods across the sub interfaces/records.
>
> Regards
> Swaranga
>
>
> On Mon, Jun 23, 2025 at 4:50 AM Brian Goetz <brian.goetz at oracle.com> 
> wrote:
>
>     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/c0f692a1/attachment-0001.htm>


More information about the amber-dev mailing list