How do AGP's `compileOptions.sourceCompatibility`/`targetCompatibility` relate to a JVM toolchain, and which one should you set?
answer
- toolchain = which JDK
- compileOptions = which bytecode
- AGP defaults source/target to toolchain
- override only to go lower
- keep jvmTarget in sync
basics
~10 sThe toolchain selects which JDK compiles; compileOptions sets the Java bytecode level (source/target). With a toolchain set, source/target default to the toolchain version, so you usually only need the toolchain.
solid answer
~40 s`compileOptions.sourceCompatibility` and `targetCompatibility` declare the Java *language level* and *bytecode target* (e.g. Java 17 features and class-file version). The JVM **toolchain** declares which *JDK* performs the compilation. They answer different questions: 'what JDK do I compile with' (toolchain) vs 'what bytecode do I emit' (compileOptions). When you set a toolchain via `jvmToolchain(17)`, AGP defaults `source/targetCompatibility` to that language version, so a single toolchain declaration is usually enough. You'd set `compileOptions` explicitly only when you need to emit *lower* bytecode than the toolchain JDK — e.g. compile with JDK 17 but target Java 11 for broader desugaring/library compatibility. In Kotlin, the parallel knob is `compilerOptions.jvmTarget`, which should be kept consistent with `targetCompatibility`.
code
kotlin · 10 linesandroid {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions { jvmTarget = "11" }
}
// Compile with JDK 17, but emit Java 11 bytecode:
kotlin { jvmToolchain(17) }go deeper
Know that compileOptions sets source/target bytecode and the toolchain selects the JDK.
Explain that AGP defaults source/target to the toolchain and that you override only to emit lower bytecode.
Reason about library-consumer compatibility, keeping Kotlin jvmTarget aligned, and the invalid 'target higher than toolchain' case.
Define org policy: standardize the toolchain JDK while allowing modules to lower targetCompatibility for binary-compatibility guarantees.
## Two orthogonal settings | Setting | Question it answers | Example value | |---|---|---| | JVM toolchain (`jvmToolchain(n)` / `java.toolchain.languageVersion`) | Which **JDK** compiles the code? | JDK 17 install | | `compileOptions.sourceCompatibility` | Which Java **language features** are allowed? | `JavaVersion.VERSION_17` | | `compileOptions.targetCompatibility` | Which **bytecode/class-file version** is emitted? | `JavaVersion.VERSION_17` | | Kotlin `compilerOptions.jvmTarget` | Bytecode target for **Kotlin** | `JvmTarget.JVM_17` | ## How they interact under AGP When a toolchain is declared, AGP and the Java plugin **default** `sourceCompatibility`/`targetCompatibility` to the toolchain's language version. So: ```kotlin kotlin { jvmToolchain(17) } // => source/target compatibility default to 17, jvmTarget aligns to 17 ``` is enough for most apps. ## When to set compileOptions explicitly You override `compileOptions` only to emit **lower** bytecode than the compiling JDK — a common case is compiling with JDK 17 but targeting Java 11 class files so the app runs on more devices/runtimes or so a library stays consumable by older toolchains: ```kotlin android { compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = "11" } // keep Kotlin in sync } kotlin { jvmToolchain(17) } // still compile on JDK 17 ``` ## Pitfalls - Setting `compileOptions` to a level *higher* than the toolchain JDK fails — you cannot emit Java 21 bytecode from a JDK 17 toolchain. - Forgetting to bump Kotlin's `jvmTarget` alongside `targetCompatibility` causes a mismatch warning/error. - Android desugaring (`isCoreLibraryDesugaringEnabled`) lets you use newer APIs at lower targets, but that's separate from the toolchain.
- Can you set targetCompatibility to 21 with a JDK 17 toolchain?No. The compiling JDK must support the requested bytecode level; targeting a version above the toolchain JDK fails. You'd need a JDK 21 toolchain.
- If you only set jvmToolchain(17) and nothing else, what bytecode is emitted?Java 17 class files — source/targetCompatibility default to the toolchain language version, and Kotlin's jvmTarget aligns to 17.
- Why might a library module set targetCompatibility lower than its toolchain?So consumers on older JDKs/toolchains can still read its class files, while the library authors still build on a newer, supported JDK.
saying these in an interview costs you the question
- Saying compileOptions and the toolchain are the same thing.
- Trying to target a higher bytecode version than the toolchain JDK provides.
- Changing targetCompatibility but leaving Kotlin's jvmTarget mismatched.