How does the JVM locate and invoke a Kotlin top-level `main`, given the JVM only knows `public static void main(String[])` inside a class?
answer
- App.kt -> AppKt file class
- Top-level main becomes a static method on the file class
- Launch the class name, not the function
- @file:JvmName renames the generated class
- suspend main gets an extra non-suspend bridge
basics
~20 sKotlin compiles a file like App.kt into a JVM class named AppKt and generates a hidden static main(String[]) method inside it. The JVM launches that class, and that generated method calls your real top-level main.
solid answer
~40 sThe JVM launcher can only invoke `public static void main(String[])` on a named class. Kotlin satisfies this by wrapping each `.kt` file's top-level declarations into a synthetic class whose default name is the file name plus `Kt` (so `App.kt` becomes `AppKt`). For a file's top-level `main`, the compiler emits a static `main(String[])` on that file class as the launchable bridge. You point the launcher at the file class — e.g. `java -cp ... AppKt`, or in Gradle set `application { mainClass.set("AppKt") }`. You can rename the generated class with `@file:JvmName("App")`, which is why many projects launch `com.example.App` even though no `App` class is written by hand. For `suspend fun main`, the compiler additionally generates a non-suspend `main` that bootstraps the coroutine and blocks until completion, preserving the plain-`main` contract the JVM expects.
code
kotlin · 9 lines@file:JvmName("Launcher")
package com.example.app
fun main(args: Array<String>) {
println("Running ${com.example.app.greeting()}")
}
fun greeting() = "KataJob"
// Launch with: java -cp app.jar com.example.app.Launchergo deeper
Recognizes that App.kt becomes some class and main runs, even if hazy on details.
States the file-facade naming (AppKt) and that you launch the class, not the function.
Explains @file:JvmName, Gradle/manifest mainClass, and the synthetic static main bridge including suspend.
Reasons about multi-target entry points, build reproducibility, and how packaging/manifest choices affect deployment and tooling.
## The mismatch Kotlin lets you write `main` as a **top-level function** with no enclosing class. But the JVM's launcher protocol is rigid: to start a program it loads a named class and calls its `public static void main(String[] args)`. There is no concept of a "top-level function" in JVM bytecode — every method lives inside a class. ## Kotlin's solution: the file class (file facade) Kotlin resolves this by compiling all top-level declarations of a `.kt` file into a synthetic class called the **file facade**. Its default name is the **file name + `Kt`**: - `App.kt` -> class `AppKt` - `Main.kt` -> class `MainKt` Top-level functions (including `main`) and top-level properties become static members of that class. So your top-level `fun main` becomes `AppKt.main(...)` in bytecode, and the compiler emits the JVM-shaped `public static void main(String[])` there. ## Pointing the launcher at it You must tell the launcher which class holds `main`: ```bash # command line java -cp app.jar AppKt ``` ```kotlin // Gradle application plugin application { mainClass.set("AppKt") // or "com.example.AppKt" with a package } ``` The jar's manifest `Main-Class` attribute holds the same fully qualified name. ## Renaming with @file:JvmName The `Kt` suffix is ugly for public entry points, so you can override the generated class name: ```kotlin @file:JvmName("App") package com.example fun main() = println("hi") ``` Now the class is `com.example.App`, and you launch `com.example.App`. This is purely a JVM-naming concern; Kotlin call sites are unaffected. ## suspend main bridging For `suspend fun main`, the JVM still needs a plain `main`. The compiler generates a non-suspend `static main(String[])` that starts the top-level coroutine and blocks the launching thread until it finishes — so the launchable contract is preserved while you still get to call suspend functions. ## Why this matters in practice - Build tools and run configs need the **file class name** (with `Kt` or your `@file:JvmName`), not the function name. - Multiple files can each have a `main`; the launched one is whichever class you configure. - This is JVM-specific. Kotlin/Native and Kotlin/JS have their own entry-point conventions. ## Key keywords/APIs - File facade / file class — `FileNameKt`. - `@file:JvmName("X")` — rename the file class. - `Main-Class` manifest attribute / Gradle `mainClass`. - Synthetic `static main(String[])` bridge.
- What is the default generated class name for top-level `main` in `Service.kt`?`ServiceKt` — file name plus the `Kt` suffix, prefixed by the package if any.
- How do you change that generated class name?Add a file-level annotation `@file:JvmName("Service")` at the very top, before the package declaration's effect on naming — it renames the file facade.
Kotlin slips your free-floating main into a JVM-shaped envelope (AppKt) so the strict JVM postman can deliver it.
saying these in an interview costs you the question
- Saying the JVM can directly launch a top-level function with no class
- Configuring Gradle `mainClass` to the function name instead of the file class
- Not knowing the `Kt` suffix convention
- Believing `@file:JvmName` changes Kotlin-side call sites
- Assuming this JVM bridging applies identically to Native/JS targets