Compare -Xjvm-default=all and -Xjvm-default=all-compatibility. What does each emit, and which would you choose when evolving a published Kotlin library?
answer
- all = real default, no DefaultImpls
- all-compatibility = real default + kept DefaultImpls
- DefaultImpls removal breaks old binaries
- Libraries: migrate via all-compatibility
- Set flag explicitly per module
basics
~20 sBoth make interface bodies into real JVM default methods so Java inherits them. 'all' drops the old helper class; 'all-compatibility' keeps it too, so code already compiled against the helper still links. For published libraries, use all-compatibility during migration.
solid answer
~30 s`-Xjvm-default=all` emits real JVM `default` methods on the interface and **stops generating** the `DefaultImpls` class. `-Xjvm-default=all-compatibility` emits the real default methods **and also** keeps `DefaultImpls` (now delegating to the default methods) so that any binary previously compiled against the DefaultImpls ABI continues to link. The trade-off is ABI surface vs. cleanliness: `all` is cleaner and is fine for application code or fresh libraries; `all-compatibility` is the safe choice when third-party or downstream modules were already compiled against your DefaultImpls-based interfaces. A typical evolution path: start `all-compatibility`, ship a major version, and only later move to `all` once all consumers have recompiled.
go deeper
Likely only knows defaults make Java inheritance work, without the ABI nuance.
Knows both modes emit real default methods; may not articulate the DefaultImpls retention reason.
Explains the ABI trade-off and the migration sequence for a published library.
Designs a cross-module/cross-version rollout policy minimizing linkage breaks and documents the major-version boundary.
## What each mode emits | Mode | Real JVM `default` on interface? | `DefaultImpls` retained? | |------|------|------| | `disable` (legacy) | No (method abstract) | Yes (holds the body) | | `all` | **Yes** | **No** | | `all-compatibility` | **Yes** | **Yes** (delegates to the default method) | ## Why DefaultImpls matters for binary compatibility When a class is compiled against a Kotlin interface in legacy mode, the resulting bytecode of an implementor references `Iface$DefaultImpls.method(this)`. If you later recompile the **interface** with `-Xjvm-default=all`, `DefaultImpls` disappears. Old binaries that still reference it will throw `NoSuchMethodError`/`NoClassDefFoundError` at runtime (a binary-incompatible change). `all-compatibility` avoids this by keeping `DefaultImpls` as a thin delegator. ```kotlin // Interface evolved to all-compatibility interface Repo { fun find(id: Long): String? = null // real default method + retained DefaultImpls delegator } ``` ## Choosing for a published library - **Greenfield library, no external compiled consumers yet** -> `all`. Cleaner output, full Java interoperability, smaller ABI. - **Existing library with consumers compiled against legacy DefaultImpls** -> `all-compatibility`. Preserves the old linkage symbols. - **Application (not a library)** -> `all` is usually fine because you recompile everything together. ## Practical caveats - Mixing modes across modules: a downstream module compiled with `disable` calling an upstream interface compiled with `all` will not find DefaultImpls — recompile downstream or use `all-compatibility` upstream. - The legacy default for the compiler historically was `disable`; newer Kotlin versions changed defaults, but for libraries you should set the flag **explicitly** rather than rely on defaults. - Newer toolchains expose a dedicated `jvmDefault` Gradle option mirroring the same modes. ## Keywords `-Xjvm-default=all`, `all-compatibility`, `DefaultImpls`, JVM `default` method, ABI/binary compatibility, `NoSuchMethodError`.
- What runtime error appears if a binary compiled against DefaultImpls links against an interface recompiled with `all`?A linkage error such as NoSuchMethodError or NoClassDefFoundError, because the referenced DefaultImpls symbol no longer exists.
- Is moving from all-compatibility to all a source-compatible or binary-compatible change?Source-compatible but potentially binary-incompatible: it removes DefaultImpls symbols, so it belongs in a major version once consumers have recompiled.
saying these in an interview costs you the question
- Saying the two modes are interchangeable
- Recommending `all` for a published library with legacy consumers
- Not knowing DefaultImpls is retained under all-compatibility
- Unaware that removing DefaultImpls is a binary-incompatible change
- Confusing source compatibility with binary compatibility