<div dir="ltr"><div dir="ltr"><div>The recent thread <em>"Java Language Enhancement: Disallow access to static members via object references"</em> 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.</div><div><br></div><div>By beefing up <code dir="ltr">java.util.ServiceLoader</code> facilities and integrating its discovery mechanism directly into the language via <strong>Static Service Traits</strong>, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "<i>growing the java language</i>" presentation and the algebraic "well-known interface" model for custom numeric types (like <code dir="ltr">Float16</code>) proposed in Joe Darcy's "<i>Paths to Support Additional Numeric Types on the Java Platform</i>" presentation.</div><div role="heading"><br></div><div role="heading">== Static Service Traits for Java ==</div><div><br></div><div>I propose a system of <strong>Static Service Traits</strong>. I use the term "Trait" advisedly: this feature adopts a rigorous <strong>Coherence Model</strong> (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.</div><div></div><div role="heading"><br></div><div role="heading">1. The <code dir="ltr">service</code> Contextual Keyword</div><div>We introduce <code dir="ltr">service</code> as a contextual modifier for interface declarations. Marking an <code dir="ltr">interface</code> as a <code dir="ltr">service</code> identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry.</div><div><br></div><div><div role="status"><span><span></span></span></div></div><div></div><div role="heading">2. Static Implementations and Extension Methods</div><ul><li><span><strong>Static Implementations:</strong></span><ul><li><span><strong>In Interface Headers:</strong> <code dir="ltr">interface MyTrait implements ServiceX<T></code>. Methods are fulfilled as <code dir="ltr">static</code>.</span></li><li><span><strong>In Class Headers:</strong> <code dir="ltr">class MyClass implements static Numeric<Float16></code>. Methods are implemented as <code dir="ltr">static</code> on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously.</span></li></ul></li><li><span><strong>Static Extension Methods:</strong> Desugared at the call site. <code dir="ltr">myInstance.method()</code> becomes <code dir="ltr">MyClass.method(myInstance)</code>. Notably, if <code dir="ltr">myInstance</code> is <code dir="ltr">null</code>, it desugars to <code dir="ltr">MyClass.method(null)</code> without an immediate <code dir="ltr">NullPointerException</code>.</span></li><li><span><strong>Ergonomic Aliases:</strong> To simplify signatures, we introduce private nested static type aliases <code dir="ltr">This</code> and <code dir="ltr">Super</code> (e.g., <code dir="ltr">static This add(This a, This b)</code>).</span></li></ul><div></div><div role="heading"><br></div><div role="heading">3. Operational Mechanics & Link-Time Integration</div><div>A <strong>ServiceLoader Controller</strong> is integrated into the JVM’s class-loading pipeline. During class definition, the Controller <strong>eagerly extracts all relevant metadata</strong> to populate the Static Service Provider Registry, including:</div><ul><li><span>Header-level <code dir="ltr">static implements</code> and <code dir="ltr">implements</code> declarations.</span></li><li><span>Service binding descriptors from <code dir="ltr">module-info.class</code>.</span></li><li><span><code dir="ltr">META-INF/services/</code> provider-configuration files.</span></li></ul><div><strong>Hierarchical Precedence Resolution:</strong> To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model:</div><ol><li><span><strong>Tier 1: Type Specialization:</strong> Most specific generic match wins</span>, applying the same scrutiny and rules currently used for existing static overloaded method resolution.</li><li><span><strong>Tier 2: Physical Locality:</strong> Provider in the same file (.jar/.class) as the caller wins.</span></li><li><span><strong>Tier 3: Loader Proximity:</strong> Nearest <code dir="ltr">ClassLoader</code> in the delegation path wins.</span></li><li><span><strong>Tier 4: Modular Topology:</strong> <code dir="ltr">Internal</code> > <code dir="ltr">Explicit</code> > <code dir="ltr">java.base</code> > <code dir="ltr">Transitive</code> > <code dir="ltr">Automatic</code>.</span></li><li><span><strong>Tier 5: Sequential Order:</strong> Final tie-breaker via Classpath order.</span></li></ol><div></div><div role="heading"><br></div><div role="heading">4. Coherence, The Orphan Rule, and Quarantining</div><div>To achieve the type-safety of a trait system, we enforce an adapted <strong>Orphan Rule</strong>: A module (or package on the classpath) must own either the <code dir="ltr">service interface</code> or the target <code dir="ltr">type</code> to define an implementation.</div><ul><li><span><strong>Coherence Enforcement:</strong> Violations in modular code trigger a <code dir="ltr">LinkageError</code>.</span></li><li><span><strong>Behavioral Continuity:</strong> Violations in classpath code trigger a load-time warning and the provider is <strong>quarantined</strong> from the Static Registry. To ensure continuity, quarantined providers <strong>remain accessible via existing <code dir="ltr">java.util.ServiceLoader</code> API calls</strong>, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch.</span></li></ul><div></div><div role="heading">5. Performance and AOT Considerations</div><div>This model transforms <code dir="ltr">ServiceLoader</code> into a link-time resolver. JIT compilers can treat service calls as direct <code dir="ltr">invokestatic</code> instructions, enabling aggressive optimization. This is highly compatible with <strong>Project Leyden</strong> and <strong>GraalVM</strong>, as precedence can be "baked" into the binary during AOT compilation.</div><div></div><div role="heading"><br></div><div role="heading">Conclusion</div><div>By transitioning <code dir="ltr">ServiceLoader</code> 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.</div><div><br></div><div><br></div><div>Thoughts?</div></div>
</div>