Explain the source-set layout that the jvm() target adds to a KMP module and where JVM-only code that uses java.* APIs must live.
answer
- jvmMain/jvmTest dependsOn commonMain/commonTest
- commonMain = common stdlib only, no java.*
- java.* code lives in jvmMain
- expect in common, actual in jvmMain bridges them
- dependency direction is platform -> common, one-way
basics
~10 sjvm() adds jvmMain and jvmTest source sets that build on top of commonMain/commonTest. Any code touching java.* APIs must go in jvmMain because common code has to compile for every target.
solid answer
~40 sDeclaring `jvm()` creates the **`jvmMain`** and **`jvmTest`** source sets, which depend on `commonMain` and `commonTest` respectively. `commonMain` holds platform-agnostic code shared by all targets and therefore can only use the Kotlin common stdlib — it cannot reference `java.*`. Anything JVM-specific (e.g. `java.io.File`, `java.time`, `java.util.concurrent`, JDBC) lives in `jvmMain`. The **`expect`/`actual`** mechanism bridges them: declare an `expect fun`/`expect class` in `commonMain`, then provide the JVM `actual` implementation in `jvmMain` using `java.*`. The dependency direction is one-way — `jvmMain` sees `commonMain`, never the reverse. Test code follows the same pattern: `commonTest` for shared tests, `jvmTest` for JVM-only ones. Gradle resolves `jvmMain` dependencies onto the JVM compilation's classpath.
code
kotlin · 6 lines// commonMain/kotlin/Platform.kt
expect val platformName: String
// jvmMain/kotlin/Platform.jvm.kt
actual val platformName: String =
"JVM " + System.getProperty("java.version") // java.* allowed herego deeper
Knows jvmMain holds JVM code and commonMain is shared.
Explains the dependsOn hierarchy and that java.* must live in jvmMain, with expect/actual bridging.
Reasons about how much to keep in common vs jvmMain and the portability tradeoffs.
Designs module boundaries so the common surface stays portable as new targets are added.
## The hierarchy jvm() creates A KMP module organizes code into **source sets**. Declaring `jvm()` adds two and wires their dependencies: - **`commonMain`** — shared, platform-agnostic code. May use only the **Kotlin common standard library**. *Cannot* reference `java.*`, because it must also compile for Native/JS/Wasm. - **`jvmMain`** — JVM-specific production code; `dependsOn(commonMain)`. Has full access to `java.*`, `javax.*`, and JVM libraries. - **`commonTest`** — shared tests. - **`jvmTest`** — JVM-specific tests; `dependsOn(commonTest)` and sees `jvmMain`. The dependency arrow always points from platform set **toward** common — `jvmMain` can see `commonMain`, but common code never sees JVM code. ## Where java.* code goes Because `commonMain` must compile for every declared target, it cannot call platform APIs. So JVM-only logic belongs in **`jvmMain`**. To call it from shared code, use **`expect`/`actual`**: ```kotlin // commonMain expect fun readConfig(path: String): String // jvmMain import java.io.File actual fun readConfig(path: String): String = File(path).readText() // java.* is fine here ``` `expect` declares the contract in common; `actual` supplies the platform body. The compiler enforces that every `expect` has a matching `actual` per target. JS/Native targets would provide their own `actual` versions. ## Practical rules - Keep as much logic as possible in `commonMain` for maximum reuse. - Push only the truly platform-bound pieces (file IO, threading primitives, framework integration) into `jvmMain`. - Don't try to `import java.*` in `commonMain` — it won't compile for other targets. - Default source directories: `src/commonMain/kotlin`, `src/jvmMain/kotlin`, etc. ## Why it matters This layout is the backbone of code sharing in KMP: the common set defines the surface, and each target (jvm, native, js, wasm) fills in its `actual`s. Getting the boundary right keeps the shared code portable.
- Why can't commonMain import java.time.LocalDate?commonMain must compile for all targets including Native/JS/Wasm, which have no java.* runtime; so java APIs are only available in jvmMain.
- How do you call a JVM-only implementation from shared code?Declare an expect function/class in commonMain and provide the actual implementation using java.* in jvmMain.
commonMain is the universal blueprint; jvmMain fills in the JVM-specific parts the blueprint left as 'to be supplied' (expect/actual).
saying these in an interview costs you the question
- Claiming commonMain can use java.* freely
- Reversing the dependency so commonMain depends on jvmMain
- Not mentioning expect/actual as the bridge
- Putting all logic in jvmMain and defeating code sharing