Category: Tutorial

The Power of Gradle Kotlin DSL

The Power of Gradle Kotlin DSL

-The following is based on Gradle 4.3.1-

A few weeks ago I started migrating most of my Groovy-based gradle.build scripts to Kotlin-backed gradle.build.kts scripts using the Kotlin DSL. Why would I do that? Kotlin is my language of choice and I love the idea of using a single language to do all my work. I never learned programming with Groovy and only know the bloody basics, which always makes me think: “This can’t be the best way to do things…”. Kotlin, on the other hand, is a language I use on a daily basis and therefore I know how to use the language appropriately. Additionally, Kotlin is a statically-typed language, whereas Groovy isn’t. IDEs are having hard times offering code completion and error detection at compile time when a Groovy build script is being edited. As for the Kotlin DSL, this isn’t true. Especially IntelliJ knows how to help us with Kotlin development, even in gradle.build.kts files. All these reasons made me take a deeper look at the new style Gradle offers.

Minor Impediments

It can sometimes be a bit tedious to rewrite your gradle.build into gradle.build.kts files, especially in the IDE with all its caches malfunctioning during that process. I often had to reopen my project or even reimport it before IntelliJ understood what was going on. It also often helps to use “Refresh all Gradle projects” button in the Gradle view.

Let’s take a look

The following snippet shows the first part of a working example. It was taken from one of my projects, which is a Kotlin web application based on the Vert.x toolkit. Learn more about the technology in this post I wrote earlier.

The script first defines a few global variables, mostly containing version numbers, which are used throughout the build file. Next, we can observe the plugins block that simply defines a few plugins used for the build. Most importantly, the Kotlin Gradle plugin for JVM applications is included, which we can do with the DSL-specific function kotlin(module: String), that takes its module argument and appends it to "org.jetbrains.kotlin.", which then is put into the id(plugin: String) method, the default api for applying plugins. Last but not least, we can see the listing of dependencies, which again provides a kotlin convenience method we can use to reduce redundant declarations. A similar approach can be seen with the definition of the io.vertx dependencies. In order to only once write the "io.vertx.vertx" String, which is part of every single Vert.x dependency, it’s used as a receiver of let. A first example of real idiomatic code within the build script.

//imports

//taken from the `plugins` defined later in the file
val kotlinVersion = plugins.getPlugin(KotlinPluginWrapper::class.java).kotlinPluginVersion
val kotlinCoroutinesVersion = "0.19.3"

val vertxVersion = "3.5.0" //
val nexusRepo = "http://x.x.x.x:8080/nexus/content/repositories/releases"

plugins {
    kotlin("jvm").version("1.2.0")
    application
    java
    `maven-publish`
}

dependencies {
    compile(kotlin("stdlib", kotlinVersion))
    compile(kotlin("reflect", kotlinVersion))
    compile("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion")

    "io.vertx:vertx".let { v ->
        compile("$v-lang-kotlin:$vertxVersion")
        compile("$v-lang-kotlin-coroutines:$vertxVersion")
        compile("$v-web:$vertxVersion")
        compile("$v-mongo-client:$vertxVersion")
        compile("$v-health-check:$vertxVersion")
        compile("$v-web-templ-thymeleaf:$vertxVersion")
    }

    compile("org.slf4j:slf4j-api:1.7.14")
    compile("ch.qos.logback:logback-classic:1.1.3")
    compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.0.pr3")

    testCompile(kotlin("test", kotlinVersion))
    testCompile(kotlin("test-junit", kotlinVersion))
    testCompile("io.vertx:vertx-unit:$vertxVersion")
    testCompile("org.mockito:mockito-core:2.6.2")
    testCompile("junit:junit:4.11")
}

// Part 2
}

The second part of the example project starts with defining repositories, which are used to find dependencies and plugins declared earlier. Again, we see an example of simplifying the code with the help of using the language: The custom Maven repositories are defined using the functional method forEach, and thus shortens the boilerplate. After that, the plugins are being configured, which for instance is necessary for enabling coroutine support or defining the application properties. Finally, we can observe a sequence of task configurations that control the behavior of single build steps, e.g. tests.

// ...Part 1

repositories {
    mavenCentral()
    jcenter()
    listOf("https://www.seasar.org/maven/maven2/",
            "https://plugins.gradle.org/m2/",
            nexusRepo).forEach {
        maven { url = uri(it) }
    }
}

kotlin {
    experimental.coroutines = Coroutines.ENABLE
}

application {
    group = "de.swirtz"
    version = "1.0.0"
    applicationName = "gradle-kotlindsl"
    mainClassName = "de.swirtz.ApplicationKt"
}

publishing {
    repositories {
        maven {
            url = uri(nexusRepo)
        }
    }
    if (!project.hasProperty("jenkins")) {
        println("Property 'jenkins' not set. Publishing only to MavenLocal")
    } else {
        (publications) {
            "maven"(MavenPublication::class) {
                from(components["java"])
            }
        }
    }
}

tasks {
    withType<KotlinCompile> {
        kotlinOptions.jvmTarget = "1.8"
    }

    withType<Test> {
        testLogging.showStandardStreams = true
    }

    withType<Jar> {
        manifest {
            attributes["Main-Class"] = application.mainClassName
        }
        from(configurations.runtime.map { if (it.isDirectory) it else zipTree(it) })
    }

    withType<GradleBuild> {
        finalizedBy("publishToMavenLocal")
    }
}

The Result

We’ve seen a rather simple build script written with the Gradle Kotlin DSL. I made use of a few idiomatic Kotlin functions in order to show the power of such .kts files. Especially for Kotlin developers, it can make much sense to completely switch to the shown approach. IntelliJ does support the creation of new build.gradle.kts files by default when you open the “New” option in “Project” view.

There will be situations, which make you want to ask somebody for help. I recommend reaching out directly in the corresponding Kotlin Slack channel: gradle.

I hope I could inspire you to give it a try! Good Luck 🙂

The whole script as a Gist

Please follow and like me 🙂
Kotlin Reified Types in Inline Functions

Kotlin Reified Types in Inline Functions

I’ve noticed that many people haven’t ever heard of reified types or have problems understanding what they are, and what they do. Therefore this little post is intended to bring some light into the darkness of Kotlin’s reified types.

Starting situation

fun <T> myGenericFun(c: Class<T>)

In an ordinary generic function like myGenericFun you can’t access the type T because it’s, like in Java, erased at runtime and thus only available at compile time.  Therefore, if you want to use the generic type as a normal Class in the function body you need to explicitly pass the class as a parameter like the parameter c in my example. That’s correct and works fine but makes it a bit unsightly for the caller.

Inlined function with reified to the rescue

If, on the other hand, you use an inline function with a reified generic type T, the value of T can be accessed even at runtime and thus you don’t need to pass the Class<T> additionally. You can work with T as if it was a normal Class, e.g. you might want to check whether a variable is an instance of T, which you can easily do like this: myVar is T.

An inline function with reified type looks like this:

inline fun <reified T> myGenericFun()

Be aware, that reified types can only be used in combination with inline functions. Such an inline function makes the compiler copy the function’s bytecode into every place where the function is being called (we say the function is being “inlined“). When you call an inline function with reified type, the compiler knows the actual type used as a type argument and modifies the generated bytecode to use the corresponding class directly. Therefore calls like myVar is T become myVar is String (if the type argument were String) in the bytecode and at runtime.

Reified in Action

Let’s have a look at an example, where reified is really helpful. We want to create an extension function for String called toKotlinObject, which tries to convert a JSON string to a Kotlin Object, specified by the function’s type T. We can use com.fasterxml.jackson.module.kotlin for this and the first approach is the following:

Compile Error

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
      //does not compile!
      return mapper.readValue(JsonObject(this).encode(), T::class.java)
}

The readValue method wants us to provide the information which type it’s supposed to parse the JsonObject to. We try to use the type parameter T and get its Class. This does not work and the compiler tells us: “Cannot use ‘T’ as reified type parameter. Use a class instead.”

Working example without reified

fun <T> String.toKotlinObject(c: Class<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(JsonObject(this).encode(), c)
}

In a next step we pass the Class of T explicitly which can directly be used as an argument to readValue. This works and is actually the same approach used in Java code for such scenarios. The function can be called like so:

"{}".toKotlinObject(MyJsonType::class.java)

With reified
Using an inline function with reified type parameter T makes it possible to implement our function as follows:

inline fun <reified T> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(JsonObject(this).encode(), T::class.java)
}

No need to pass the Class of T additionally, T can be used as if it was an ordinary class. For the client the code looks like this:

"{}".toKotlinObject<MyJsonType>()

Important
Inline reified functions are not callable from Java code, whereas normal inline functions are. That’s probably the reason why not every type parameter used in inline functions is reified by default.

Conclusion

This was just a quick introduction to reified types. In my opinion the call to a function with reified types looks way better because we can make use of the <> syntax commonly used whenever generics are relevant. As a result, it’s more readable than the Java approach of passing a Class object as a parameter. All the dirty details can be read in this specification document.

If you want to read more about Kotlin’s beautiful features I recommend the book Kotlin in Action to you and also like to direct you to my other articles 🙂

Keep coding!

Cheers, Simon.

Please follow and like me 🙂
Kotlin Conventions – Operator Overloading

Kotlin Conventions – Operator Overloading

Operator Overloading and Conventions in Kotlin

Introduction

Kotlin supports a technique called conventions, everyone should be familiar with. For example, if you define a special method plus in your class, you can use the + operator by convention.
In this article I want to show you which conventions can be used and will provide some Kotlin code demonstrating the concepts of course.

Read More Read More

Please follow and like me 🙂

Enjoy this blog? Please spread the word :)