What does `requires static` mean, and when would you use an optional, compile-time-only module dependency?
answer
- static = compile-time required, runtime optional
- like Maven provided / Gradle compileOnly
- classic use: SOURCE/CLASS-retention annotations
- app launches even if the module is absent at runtime
- guard runtime use or risk NoClassDefFoundError
basics
~20 srequires static X makes module X required when compiling but optional at run time. It is for things like annotations or optional integrations that you compile against but that do not need to be present when the app runs.
solid answer
~50 s`requires static X` declares an **optional dependency**: X must be present (readable) at **compile time**, but the runtime does **not** require X to be on the module path — the module resolves and launches fine if X is absent. This is the JPMS equivalent of Maven's `provided`/`optional` scope. Two main uses: (1) **compile-time-only artifacts** such as annotations with `SOURCE`/`CLASS` retention (e.g. a nullability or processor annotation library) that you never need at runtime; (2) **optional features** where your code uses another module only if it happens to be available, guarding such use reflectively or with careful class-load isolation. The catch: because X may be missing at runtime, any code path that actually touches X's types will throw `NoClassDefFoundError` if X isn't there — so you must guard those paths. You can also combine modifiers: `requires static transitive`.
code
java · 10 linesmodule com.acme.service {
// Annotations only the compiler/processor needs; absent in production:
requires static org.example.annotations;
// Optional integration: present if available, app starts regardless
requires static com.acme.optional.metrics;
}
// At runtime, touching com.acme.optional.metrics types when the module
// is absent -> NoClassDefFoundError. Guard such code paths.go deeper
Knows it exists and means an optional/compile-only dependency, even if hazy on details.
States compile-required, runtime-optional and gives the annotations use case.
Explains the NoClassDefFoundError risk and the need to guard runtime use, and maps it to provided/compileOnly scopes.
Designs optional-feature module boundaries safely, isolating static-required code so absence degrades gracefully rather than crashing.
## The default: mandatory at compile and run time A plain `requires X` means X must be present **both** when you compile and when you run. The module system performs **resolution** at startup, building the module graph from your root module's `requires` edges; if any required module is missing, the application **fails to launch** with a resolution error. This guarantees "reliable configuration" — no surprise missing dependency mid-run. ## The `static` modifier: compile-time mandatory, runtime optional `requires static X` relaxes the runtime half of that contract: - **At compile time:** X is still required — you can (and do) compile against X's exported types. - **At run time:** X is **optional**. Resolution does **not** pull X in just because of a `static` edge, and the app launches even if X is absent from the module path. So a `static` requirement is an **optional dependency**: present for the compiler, possibly absent for the JVM. (It is the JPMS analogue of Maven `provided` / `optional` or Gradle `compileOnly`.) ## Use case 1: compile-time-only annotations The textbook case. Annotation libraries with `@Retention(SOURCE)` or `@Retention(CLASS)` are needed only by the compiler / annotation processors and are **not** loaded at runtime. Example: a nullness-checking library, or Lombok-style/processor annotations. ```java module com.acme.app { requires static org.example.annotations; // used in source, gone at runtime } ``` Shipping that annotation module to production would be dead weight; `static` keeps it out of the runtime graph while still letting you compile. ## Use case 2: optional integrations You may want to use module X *if it is available* (a pluggable backend, an optional metrics library). You compile against X, but the deployment might not include it. With `requires static X` the app still starts; you must then **guard** any use of X — typically by attempting the call inside a try/catch for `NoClassDefFoundError`/`ClassNotFoundException`, or by checking availability reflectively before touching X's types. ## The danger you must manage Because X can be missing at runtime, **executing code that references X's types when X is absent throws `NoClassDefFoundError`**. The compiler will not warn you — it saw X at compile time. So `requires static` shifts responsibility to you: keep X-dependent code on cleanly separable, guarded paths. ## Combining modifiers Modifiers compose. `requires static transitive X` means: optional at runtime **and** its (compile-time) readability is implied to consumers. This is rare but valid — e.g. re-exporting an optional compile-time annotation dependency. ## Summary table | Directive | Compile time | Run time | |---|---|---| | `requires X` | required | required | | `requires static X` | required | optional | | `requires transitive X` | required + implied to consumers | required + implied |
- What happens at runtime if a `requires static` module is genuinely absent and your code calls into it?The JVM throws NoClassDefFoundError (or ClassNotFoundException) when it first tries to load the missing type. The compiler gives no warning because the module was present at compile time, so you must guard such paths.
- How does `requires static` map to build-tool dependency scopes?It is the JPMS equivalent of Maven's provided/optional scope or Gradle's compileOnly — the dependency is on the compile path but excluded from the runtime module graph.
requires static is like training wheels you bolt on to assemble and test the bike (compile), then remove before the race (runtime) — the bike still rides without them, as long as you don't try to lean on wheels that aren't there.
saying these in an interview costs you the question
- Thinking static means the dependency is never needed at compile time
- Assuming the runtime will still resolve and add the module
- Calling into a static-required module unconditionally without a guard
- Confusing static (optional dependency) with Java's static keyword