What does @ParametersAreNonnullByDefault (and JSR-305 default annotations) do for Kotlin interop, and how would you override it for one nullable parameter?
answer
- Default: all params @Nonnull in scope
- Usually applied in package-info.java
- Per-param @Nullable overrides the default
- Built on @TypeQualifierDefault(PARAMETER)
- -Xjsr305=ignore|warn|strict tunes enforcement
basics
~20 sIt tells Kotlin that, unless marked otherwise, every parameter in that scope is non-null, so you don't have to annotate each one. To make a single parameter nullable, you add @Nullable to just that parameter.
solid answer
~40 s@ParametersAreNonnullByDefault (a JSR-305 javax.annotation meta-annotation) sets a default: within the annotated package or class, all method/constructor parameters are treated as @Nonnull unless individually overridden. Kotlin honors this default, so those Java parameters import as non-null T instead of platform types — without per-parameter annotations. It's commonly applied at package level via a package-info.java file, or per class. To override one parameter you place @Nullable directly on it, which wins over the default and imports that parameter as T?. The general mechanism is JSR-305's @TypeQualifierDefault, which @ParametersAreNonnullByDefault is built from; you can define your own defaults scoped to ElementType.PARAMETER, METHOD (return), FIELD, etc. Kotlin also respects the -Xjsr305 compiler flag to control how strictly these JSR-305 defaults are enforced (ignore/warn/strict), letting teams migrate gradually.
code
kotlin · 7 lines// Java package annotated @ParametersAreNonnullByDefault:
// void connect(String host, @Nullable String token);
fun useApi(api: Client) {
api.connect("db.local", null) // host non-null, token String?
// api.connect(null, "t") // would NOT compile: host is non-null
}go deeper
Recognizes it makes parameters non-null by default so you skip per-parameter annotations.
Knows it's package/class scoped, applied via package-info.java, and that @Nullable overrides one parameter.
Explains the @TypeQualifierDefault machinery, that it targets PARAMETER only (not returns), and the -Xjsr305 strictness levels.
Designs a migration strategy (ignore -> warn -> strict, @UnderMigration), decides default policy for a published API, and weighs JSR-305 vs JSpecify for long-term direction.
## The motivation Annotating every single Java parameter with `@Nonnull` is tedious. JSR-305 provides **default** annotations that flip the assumption for a whole scope. ## `@ParametersAreNonnullByDefault` This annotation (`javax.annotation.ParametersAreNonnullByDefault`) declares: *within this scope, every parameter is `@Nonnull` unless individually annotated otherwise.* It's typically applied: - at **package** level in `package-info.java`, or - on a **class**. ```java // package-info.java @ParametersAreNonnullByDefault package com.example.api; import javax.annotation.ParametersAreNonnullByDefault; ``` Kotlin honors this: every parameter of every method in `com.example.api` now imports as a **non-null** Kotlin type rather than a platform type. ## Overriding for one parameter A per-element annotation **wins** over the default. To make a single parameter nullable: ```java // in a class/package under @ParametersAreNonnullByDefault void configure(String host, @Nullable String proxy) { ... } ``` From Kotlin, `host` is `String` (from the default) and `proxy` is `String?` (the explicit override): ```kotlin obj.configure("h", null) // ok: proxy is String? obj.configure("h", "p") // ok obj.configure(null, "p") // COMPILE ERROR: host is non-null ``` ## The underlying machinery: `@TypeQualifierDefault` `@ParametersAreNonnullByDefault` is itself meta-annotated with JSR-305's `@TypeQualifierDefault(ElementType.PARAMETER)` plus `@Nonnull`. You can build your own scoped default — e.g. apply a non-null default to **return values** with `ElementType.METHOD`, or to fields with `ElementType.FIELD`. Kotlin recognizes these custom defaults too. There's also `@ParametersAreNullableByDefault` for the opposite policy. ## Strictness: the `-Xjsr305` flag Kotlin lets you tune how JSR-305 annotations (including these defaults) are enforced via the compiler flag: - `-Xjsr305=ignore` — treat them as platform types (no effect), - `-Xjsr305=warn` — surface mismatches as warnings, - `-Xjsr305=strict` — enforce them as real Kotlin nullability (errors). You can also scope strictness per annotation FQ name (e.g. `-Xjsr305=under-migration:warn` plus a `@UnderMigration` marker), enabling a gradual migration where a team tightens enforcement annotation-by-annotation. ## Why it matters Defaults let a Java library author make an entire API non-null-by-convention with a single package annotation, eliminating platform types wholesale at the boundary — and Kotlin consumers get full null safety for free, with `-Xjsr305` as a migration lever.
- Where is @ParametersAreNonnullByDefault usually placed to cover a whole package?In a package-info.java file, annotating the `package` declaration. Kotlin then treats all parameters in that package as non-null by default.
- How can a team adopt JSR-305 defaults gradually without breaking compilation?Use -Xjsr305=warn (or under-migration:warn with @UnderMigration markers) so mismatches are warnings, then flip to strict once the code base is clean.
- Does @ParametersAreNonnullByDefault affect return values?No. It targets ElementType.PARAMETER only. Returns need a different default (e.g. a custom @TypeQualifierDefault(METHOD)) or per-method @Nonnull/@Nullable.
saying these in an interview costs you the question
- Claiming the default also makes return types non-null
- Saying you cannot override the default for a single parameter
- Not knowing it's typically applied via package-info.java
- Being unaware of the -Xjsr305 strictness flag
- Confusing @ParametersAreNonnullByDefault with @NonNullApi-only semantics or thinking it's a Kotlin annotation