Re: [External] : Re: Proposal: Static Service Traits—Enhancing Java’s Static Polymorphism and ServiceLoader Facilities

Ron Pressler ron.pressler at oracle.com
Wed Jan 28 22:08:32 UTC 2026


What is the problem you’re trying to point out? Is it “something that can be expressed in Java could be expressed more elegantly”?

I’m not saying this is a disqualified problem, but I’d like to understand if there’s a problem here that can be stated without hinting at a specific solution (such as "mechanism X and Y could be unified"), as in “processing pure data in Java is cumbersome”, or “calling native code from Java is tedious and error-prone”, or “it’s difficult in Java to clear, debuggable/proflable concurrent code that scales well”, or “processing some Java collections incurs many cache-misses that impact performance”, or “propagating timeouts and cancellation across dependent tasks is difficult”, or “correctly freeing OS resources in the presence of exceptions is difficult” etc. (but with more elaboration, obviously).

— Ron

> On 28 Jan 2026, at 17:50, Ethan McCue <ethan at mccue.dev> wrote:
> 
> I am sympathetic because "service loader" was also what entered my mind when thinking about "implementation of interface made magically discoverable."
> 
> Right now an implementation of interface provided via the service loader
> 
> 1. Can have 0-N implementations provided
> 2. Has these provided interfaces specified in module info via "provides ... with ...;"
> 
> An implementation of an interface provided via a witness
> 
> 1. Should only have 1 implementation for a type being "summoned"
> 2. Has these provided interfaces specified inside a class, in the current prototype via "__witness"
> 3. Relies on the interface for which a witness is provided having a generic parameter
> 
> My gut feeling is that these mechanisms *could* be unified
> 
>      provides Formatter for List<String> with SomeClass;
> 
> And that, if they aren't, we'd start to see things like
> 
>     summon(Logger<App>).log(...)
> 
> But equally I find it hard to 
> 
> 1. Reason about how moving "witness registration" from classes to a module info would affect things like witness resolution or "unconditional provides."
> 2. Justify it beyond a vague feeling
> 
> 
> 
> On Wed, Jan 28, 2026, 11:56 AM Ron Pressler <ron.pressler at oracle.com> wrote:
> The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It’s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today.
> 
> I’m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem.
> 
> What was the problem you *personally* ran into? How bad were its implications? How did you work around it?
> 
> With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they’ll be able to propose a solution, and that’s would be the time to try it out and discuss it.
> 
> — Ron
> 
> 
> 
> > On 28 Jan 2026, at 00:28, Steffen Yount <steffenyount at gmail.com> wrote:
> > 
> > The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java’s compile-time and link-time static polymorphism.
> > 
> > By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed  in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation.
> > 
> > == Static Service Traits for Java ==
> > 
> > I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types.
> > 1. The service Contextual Keyword
> > We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry.
> > 
> > 2. Static Implementations and Extension Methods
> >     • Static Implementations:
> >         • In Interface Headers: interface MyTrait implements ServiceX<T>. Methods are fulfilled as static.
> >         • In Class Headers: class MyClass implements static Numeric<Float16>. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously.
> >     • Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException.
> >     • Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)).
> > 
> > 3. Operational Mechanics & Link-Time Integration
> > A ServiceLoader Controller is integrated into the JVM’s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including:
> >     • Header-level static implements and implements declarations.
> >     • Service binding descriptors from module-info.class.
> >     • META-INF/services/ provider-configuration files.
> > Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model:
> >     • Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution.
> >     • Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins.
> >     • Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins.
> >     • Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic.
> >     • Tier 5: Sequential Order: Final tie-breaker via Classpath order.
> > 
> > 4. Coherence, The Orphan Rule, and Quarantining
> > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation.
> >     • Coherence Enforcement: Violations in modular code trigger a LinkageError.
> >     • Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch.
> > 5. Performance and AOT Considerations
> > This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation.
> > Conclusion
> > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries—fulfilling the goals of both Darcy and Goetz—while maintaining the performance and stability characteristics of the modern JVM.
> > 
> > 
> > Thoughts?
> 



More information about the amber-dev mailing list