Applying the Kotlin Gradle Plugin
One line in plugins {} turns a plain Gradle build into a Kotlin/JVM project.
Every Kotlin/JVM project starts with one decisive line inside the plugins {} block: kotlin("jvm") version "2.1.10". The kotlin(...) call is a helper provided by the Gradle Kotlin DSL that expands to the fully-qualified plugin id org.jetbrains.kotlin.jvm. Applying it is what teaches Gradle how to understand .kt files at all โ without it, Gradle only knows about Java, resources, and whatever other plugins you have declared. The version string pins the Kotlin compiler and toolchain, so everyone who builds the project, and your CI server, compiles with exactly the same Kotlin release.
Applying the plugin does three things that matter day to day. First, it registers Kotlin source sets, so src/main/kotlin and src/test/kotlin become real, compilable source directories alongside the Java ones. Second, it creates the compilation tasks: compileKotlin for main code and compileTestKotlin for tests, and it wires them into the standard lifecycle so that running build or assemble invokes the Kotlin compiler automatically. Third, it adds the Kotlin standard library (kotlin-stdlib) to your dependencies for you. Since Kotlin 1.4+ the stdlib is applied by default, so you do not write an explicit dependency line for it โ the functions you rely on like let, apply, listOf, and the whole collections API come along automatically.
You configure the compiler through the kotlin { } extension that the plugin contributes. The modern, idiomatic way to choose your bytecode target is the JVM toolchain: kotlin { jvmToolchain(21) } tells Gradle which JDK to compile and run against. If that JDK is not already installed locally, Gradle can provision it automatically โ but only when a toolchain resolver is configured, such as the Foojay plugin (id("org.gradle.toolchains.foojay-resolver-convention")) declared in settings.gradle.kts. This toolchain approach replaces older, brittle approaches that hard-coded sourceCompatibility or set jvmTarget by hand, and it guarantees the same Java version across every machine that touches the build.
Beyond the core jvm plugin, JetBrains ships companion compiler plugins applied the same way through the kotlin(...) helper. kotlin("plugin.serialization") enables kotlinx.serialization, generating efficient serializers at compile time for any class you mark with @Serializable โ you still add the kotlinx-serialization-json runtime as a dependency, but the plugin is what makes @Serializable actually do something. kotlin("plugin.spring") is the all-open plugin preconfigured for Spring: it automatically makes classes annotated with @Component, @Service, @Configuration, and friends open, because Kotlin classes are final by default and Spring needs to create proxy subclasses. There is a matching kotlin("plugin.jpa") (the no-arg plugin) for Hibernate/JPA entities.
When you apply several Kotlin plugins, declare the version only once on the first kotlin(...) line and omit it on the rest โ the companion Kotlin plugins inherit the version of the core Kotlin plugin, so repeating it is redundant and risks drift. In a multi-module build the cleanest pattern is to put the version in the root project (often with apply false) or, better, in a Gradle version catalog (gradle/libs.versions.toml) so plugin and library versions live in one place. This lesson is read-along: Gradle builds do not run in the in-browser editor, so focus on recognizing what each line contributes rather than executing it.
A quick mental model: settings.gradle.kts names the build and its modules and is where you centralize plugin repositories (and the toolchain resolver); build.gradle.kts applies plugins and declares dependencies; and libs.versions.toml is the optional but recommended catalog that gives you type-safe accessors like alias(libs.plugins.kotlin.jvm). Master these three files and you can read almost any modern Kotlin/JVM project at a glance.
plugins {// Adds Kotlin/JVM support: compileKotlin task + stdlib, version pins the compilerkotlin("jvm") version "2.1.10"}repositories {mavenCentral()}kotlin {// Modern way to pick the JDK to compile/run against// (Gradle can download it if a toolchain resolver is configured in settings.gradle.kts)jvmToolchain(21)}dependencies {// No kotlin-stdlib line needed โ the plugin adds it automatically since Kotlin 1.4testImplementation(kotlin("test"))}
A complete, idiomatic build.gradle.kts for a Kotlin/JVM project.
plugins {kotlin("jvm") version "2.1.10" // version declared once herekotlin("plugin.serialization") // enables @Serializable (version inherited)kotlin("plugin.spring") // all-open: opens @Component/@Service classes}dependencies {// The serialization plugin needs its runtime to actually (de)serialize JSONimplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")}
Companion compiler plugins applied via kotlin(...); declare the version only once.
# gradle/libs.versions.toml โ central version catalog[versions]kotlin = "2.1.10"[plugins]kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }# then in build.gradle.kts:# plugins {# alias(libs.plugins.kotlin.jvm)# alias(libs.plugins.kotlin.serialization)# }
A version catalog keeps the Kotlin version in one place for multi-module builds.
๐ง Check your understanding
0/1 ยท 0/1 answered1. After adding kotlin("jvm") version "2.1.10" to your plugins {} block, which dependency do you normally NOT need to declare manually?