skip to content

Compare Kotlin's program-entry model to Java's, and discuss design implications of allowing a top-level / expression-body `main`.

level: principalimportance: nice to knowfreq 25%

answer

  1. Kotlin: top-level, no static/public, expression body OK
  2. Java: class + static + public main
  3. Win: minimal ceremony; one-line program
  4. Cost: tooling targets file-class name, not the function
  5. @file:JvmName + mainClass make it deployable cleanly

basics

~20 s

Java forces main to be a static method inside a class; Kotlin lets it be a free top-level function and even a one-line expression body. This reduces ceremony, but you must point your tooling at the generated file class instead of a function name.

solid answer

~50 s

Kotlin's entry point is a top-level function — no enclosing class, no `static`, no `public` boilerplate — and can use an expression body (`fun main() = run()`). Java historically required `public static void main(String[])` inside a class (only relaxed by recent preview features). The design win is removing accidental complexity for scripts, demos, and small tools: the smallest runnable Kotlin program is one line. The trade-off is indirection at the JVM layer: the compiler synthesizes a file facade (`AppKt`) and a static `main` bridge, so build/run configuration references the class name, not the function. At scale this is mostly invisible, but it shapes how you name files, use `@file:JvmName`, configure `mainClass`, and reason about multiple `main`s in a module. Across Kotlin/Native and Kotlin/JS the same top-level `main` source works while the platform supplies its own launch mechanics.

code

kotlin · 9 lines
kotlin
@file:JvmName("Application")
package com.example

// Expression-body, top-level entry point
fun main(args: Array<String>) = startServer(args)

fun startServer(args: Array<String>) {
    println("server up with ${args.size} args")
}

go deeper

for a junior

Notes Kotlin's main needs no class and can be a one-liner, unlike Java.

for a middle

Contrasts removed static/public/class ceremony and knows a file class is generated.

for a senior

Explains expression-body Unit contract, @file:JvmName, and configuring the launch class for deployment.

for a principal

Weighs ergonomics vs. discoverability/tooling trade-offs, multiplatform uniformity, and framework-owned entry points in real architectures.

## The two models side by side ```java // Java (classic): ceremony required public class App { public static void main(String[] args) { System.out.println("hi"); } } ``` ```kotlin // Kotlin: top-level, expression body, one line fun main() = println("hi") ``` Kotlin removes three obligations Java imposed: the **enclosing class**, the `static` modifier, and the `public` modifier (top-level functions are public by default). It also allows an **expression body** — `fun foo() = expr` — so `main` can be a single expression with no braces or `return`. ## Why Kotlin made this choice - **Lower ceremony for small programs.** Scripts, examples, and CLIs become trivially short. This matches Kotlin's pragmatic, boilerplate-averse philosophy. - **Consistency with top-level declarations.** Kotlin already allows top-level functions and properties; `main` is just one of them, not a special case requiring a class. - **Multiplatform uniformity.** The same `fun main()` source compiles for JVM, Native, and JS, even though each platform's actual launch path differs. ## The cost: JVM-layer indirection Because the JVM has no top-level functions, the compiler builds a **file facade** class (`App.kt` -> `AppKt`) and emits a synthetic `static main(String[])` bridge. Consequences a tech lead must internalize: - **Tooling targets the class name**, e.g. Gradle `application { mainClass.set("com.example.AppKt") }` or the jar manifest `Main-Class`. Getting this wrong is a common "no main manifest attribute" failure. - **`@file:JvmName`** is the lever to expose a clean class name (`com.example.App`) for published artifacts. - **Multiple `main`s** can exist across files in one module; you must explicitly choose which the artifact launches. - **`suspend fun main`** adds another synthetic bridge that bootstraps and blocks on a coroutine. ## Expression-body subtlety An expression-body `main` returns whatever the expression returns, but for an entry point that should still effectively be `Unit`. `fun main() = println("hi")` works because `println` returns `Unit`. Writing `fun main() = 42` is not a valid entry point because the entry contract is a `Unit`-returning `main`. ## Design implications at scale - **Discoverability:** a free function is easy to write but slightly harder to locate than a conventional `App.main`; teams often standardize on an `Application.kt` / `@file:JvmName("Application")`. - **Frameworks override this:** Spring Boot Kotlin apps use `runApplication<App>(*args)` inside `main`, and Android/desktop frameworks own their own entry mechanics, so the raw `main` is often a thin launcher. - **Reproducible builds/deploys** depend on a stable, explicitly configured main class — never rely on accidental defaults. ## Key keywords/APIs - Top-level function, expression body (`= expr`). - File facade `AppKt`, `@file:JvmName`. - Gradle `mainClass`, manifest `Main-Class`. - `runApplication` (Spring), `suspend fun main` bridge.

  • Is `fun main() = 42` a valid entry point?
    No. The entry contract requires a `Unit`-returning `main`; an `Int`-returning expression body isn't recognized as the program entry point.
  • How do Spring Boot Kotlin apps typically structure `main`?
    A thin top-level `fun main(args: Array<String>) { runApplication<App>(*args) }`, delegating bootstrapping to the framework.

Java makes you build a whole house (class) to hang one door; Kotlin lets the door stand on its own — but the postal service (JVM) still needs a street address, so the compiler assigns one (AppKt).

saying these in an interview costs you the question

  • Claiming Kotlin's top-level main has no JVM-class representation at all
  • Saying any expression-body main (e.g. returning Int) is a valid entry point
  • Ignoring that deployment requires an explicitly configured main class
  • Believing the reduced ceremony has zero tooling cost
  • Assuming Java never allowed any simplification (recent previews do)

context