Private static members in sealed-interfaces?

Brian Goetz brian.goetz at oracle.com
Mon Jun 23 16:03:00 UTC 2025


OK, good.  SO the good news is that we agree that the current asymmetric 
treatment of private methods-but-not-other-members in interfaces should 
be fixed.  The bad news is that it is not anywhere near the top of 
anyone's priority list :(



On 6/23/2025 11:12 AM, Swaranga Sarma wrote:
> Ah, yes. When you said members, I was thinking of non-static instance 
> fields. Yes, this is all 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.
> Thank you.
>
> > 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.)
> Yes, a much better way to say what I was trying to emphasize with my 
> example using the sealed classes hierarchy.
>
> Regards
> Swaranga
>
>
> On Mon, Jun 23, 2025 at 5:19 AM Brian Goetz <brian.goetz at oracle.com> 
> wrote:
>
>     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/738c1d34/attachment-0001.htm>


More information about the amber-dev mailing list