On 4/20/2023 8:03 AM, Josiah Noel wrote:
We're talking about a case where module Y's primary purpose is not to
implement SPI from X, but to provide its own packages/functionality
unrelated to X. The classes in Y that use X should not be exported, and
should only be loaded when X service-loadsĀ them. Hence, the Y module
will use requires static to clearly inform that the classes are required
at build time, but not for Y's normal operation.
We understand the scenario, which involves the module system allowing
optionality not only of service providers for a given service, but also
of the service itself.
However, as Alan has been saying, `requires static` doesn't have the
precise, clear meaning that you think. When the module system sees the
directive `requires static X;` in some module Z, the module system
doesn't know the reason for the directive:
- Perhaps code in Z is annotated using annotation interfaces from X.
- Perhaps code in Z is going to reflect over the classes in X.
- Perhaps code in Z is implementing X's exported interfaces and the
owner of Z is really sure that whenever Z is resolved, X is also
resolved. (No use of ServiceLoader is intended in this item.)
You're going to say, "But that's Z, not Y! Y doesn't do any of those
things! Y uses `requires static X;` to get a light touch on a service in
X, and Y's service provision should be considered optional if X isn't
available."
But Y and Z are the same to the module system -- each directs the module
system to allow itself to be resolved even if X is not resolved. The
module system then sees that Y wants to provide a service, but the
module system has no idea where the service is, so fails fast at
startup. This is a feature, not a bug. The classpath behavior, of only
discovering that a service provider can't provide a service during run
time (because the service is missing), is not what we wanted for modular
services. If `provides` directives were treated as optional, i.e.,
ignored when the service can't be found at startup, people would say
"Why bother with `provides`? It's no more reliable than putting
everything on the classpath, when ServiceLoader throws because it can't
instantiate the service provider."
So, I hope you can see why we are extremely cautious about acceding to
requests to relax the checking of `provides`. In your scenario, Y is
pulling double duty, which makes it harder to understand overall, and
you'll benefit a lot more from breaking it up than from a more relaxed
module system.
Alex