Just when you were getting comfortable with a programming language, the rug gets pulled from under your feet.
From version 8.2, Gradle is now Kotlin first. That means Groovy is no longer the default language when creating new Gradle projects with gradle init
.
Does that mean you should rush to migrate your Gradle build to Kotlin?
Not necessarily. Both languages are still supported, but there are some big advantages to working with Kotlin build scripts.
The Kotlin DSL (domain specific language):
- is easier to work with in the IDE
- has a type-safe API
- means you’re working with a modern statically typed language
Yes, it’s frustrating to have to learn a new build script syntax. But to help you pick up the Gradle Kotlin DSL as fast as possible, here are 5 Kotlin language features you need to know.
1. A language built for Java developers
Kotlin is a language that helps Java developers write better code.
Most of its features, like built in null safety and data classes aren’t that relevant to writing Gradle build scripts.
But one that is relevant is the fact that Kotlin is statically typed. Any variable you declare must have a specific type.
You don’t always have to provide the type because Kotlin works it out from the assigned value, like when declaring a String variable.
val todaysDestination = "The theme park"
How’s it used in Gradle?
Since Kotlin is statically typed, when you edit Gradle build scripts, IntelliJ IDEA knows what type of thing it’s dealing with.
That means you get:
- useful autocomplete suggestions
- syntax highlighting
- relevant warnings and errors
Compared to the dynamically typed Groovy DSL, editing the Kotlin DSL is a much smoother experience for developers.
2. Better strings
If you’re like me and get upset by inconsistencies in code, then Kotlin strings make life a bit easier.
There are 2 types:
- double quoted (escaped strings)
- triple quoted (multiline strings)
Double quoted strings are the old classic, familiar to any Java developer.
val bestRide = "Star Flyer"
The difference in Kotlin is that you can simply put a variable inside a string to interpolate it.
val bestRide = "Star Flyer"
println("Best ride at Tivoli Gardens is $bestRide")
// prints Best ride at Tivoli Gardens is Star Flyer
Triple quoted strings, like in Java, are an easy way to define a multi-line string without messing about with newline characters.
val tivoliGardensEssentials = """
Location Copenhagen, Denmark
Coordinates 55°40′25″N 12°34′06″E
Opened 15 August 1843; 180 years ago
"""
OK, but let’s see how this reduces inconsistencies.
How’s it used in Gradle?
If you ever worked in a Groovy build script, you probably know that you can define Groovy strings with either single or double quotes. This often leads to horrible inconsistencies that send my head in a spin.
In Kotlin build scripts, strings are always double quoted. So simple!
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
}
So far, I haven’t seen any need to use triple quote strings in build scripts, but string interpolation can come in handy.
In this example, we define a copy task whose destination directory is within the build directory.
tasks.register<Copy>("copyMe") {
from("golden-tickets")
into("$buildDir/golden-tickets")
}
Here, the buildDir variable comes from the Project object, which Gradle makes available to your build script. You can of course use any local variable you like.
3. Kotlin build scripts are special
Kotlin scripts are files with a .kts extension that you can run without compiling or packaging them.
In IntelliJ IDEA, you can just right-click, select Run, and see the results of your script file before your eyes.
Compare that with Java’s requirement to have an overly-verbose public static void main
method within a class, and you can see why scripting with Kotlin is appealing.
What’s even more interesting, is that Kotlin scripts can come in specific flavours, each with their own custom context and classpath.
How’s it used in Gradle?
The Gradle build script is, you guessed it, a Kotlin script.
That means you can execute arbitrary Kotlin code in build.gradle.kts. But what makes it really powerful is the Gradle context in which it’s executed.
The Gradle build script context means:
- Any functions you call are automatically executed on the org.gradle.api.Project object e.g. repositories, dependencies.
- Default imports are applied for commonly used classes, so you can refer to class names directly. e.g. define a task of type Copy, one of Gradle’s built-in class types.
- Applying plugins exposes different functions to call in the rest of the build script e.g. applying the java plugin means you can call java and configure the toolchain.
Of course, Gradle sets up this context programmatically in the background. That’s why you must execute your build script via the Gradle wrapper with ./gradlew <task-name>
rather than directly.
4. Lambda expressions everywhere
Chances are you already used lambda expressions in Java. They’re handy inline functions that you can pass around like variables.
Kotlin also has lambda expressions, and they look like this:
val enterThemePark = { themeParkName: String ->
println("Entering $themeParkName")
}
This defines an enterThemePark lambda expression which takes a single string argument. In Kotlin, lambda expressions are always surrounded by curly brackets.
You call the lambda like any regular function.
enterThemePark("Tivoli Gardens") // prints Entering Tivoli Gardens
How’s it used in Gradle?
Gradle uses lambda expressions everywhere, which is why it’s so important to understand how they work.
Whenever you see curly brackets in your Kotlin build script, it’s probably a lambda expression.
Take the repositories configuration, for example.
repositories {
mavenCentral()
}
This is nothing but a repositories function which takes a lambda expression as its argument. Gradle keeps hold of the lambda expression and executes it when it reaches the execution phase of your build.
The same is true for the dependencies configuration. Just another plain old function call with a lambda expression.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
}
You can tell if a function takes a lambda expression by navigating to source and looking for () -> kotlin.Unit
in the function declaration.
Yeah, the Gradle build script looks like some kind of configuration file, but it’s really just clever use of function calls taking lambda expressions.
5. Lean code with infix functions
The more code looks like natural language, the easier it is to read. So the theory goes.
That’s why Kotlin has a trick up its sleeve to replace the dot and brackets (a.k.a. parentheses) you normally use to call a function with spaces. Infix functions are a special type of function to help you write clean code.
Here’s an example of a built-in Kotlin infix function to, which you can use to define a map.
val topEuropeanAmusementParks = mapOf(
1 to "Port Aventura",
2 to "Parc Asterix",
3 to "Tivoli Gardens"
)
So what would be 1.to("Port Aventura")
can be simplified to 1 to "Port Aventura"
.
Same number of characters. Less stuff to read.
How’s it used in Gradle?
If you’ve ever used Gradle’s Groovy DSL, you’ll be familiar with the distinct lack of brackets.
Gradle has introduced a similar concept in parts of the Kotlin DSL using infix functions. A good example of this is defining a plugin version.
plugins {
java
id("org.springframework.boot") version "3.1.2"
id("io.spring.dependency-management") version "1.1.2"
}
The version function is an infix function, so we can leave out the dot and brackets.
Whether this improves understanding is debatable, since it can be confusing to have different function calls use a different syntax. But you can check if a specific function is an infix function by navigating to source to view the signature.
Here’s the function signature for version:
public infix fun org.gradle.plugin.use.PluginDependencySpec.version(version: kotlin.String?): org.gradle.plugin.use.PluginDependencySpec
When you see the infix
keyword, it’s time to hit the spacebar.
Final thoughts
Are there other Kotlin language features you’ll need to know?
Probably, but these are the most important ones where understanding them makes the build script easier to digest and less magical.
If you haven’t used Kotlin before, fire up IntelliJ’s Kotlin console under Tools > Kotlin > Kotlin REPL and try out some of the examples above.
Stop reading Gradle articles like these
This article helps you fix a specific problem, but it doesn't teach you the Gradle fundamentals you need to actually help your team succeed.
Instead, follow a step-by-step process that makes getting started with Gradle easy.