Matthew Tyson
Contributing writer

Kotlin for Java developers

how-to
Nov 13, 202410 mins
JavaProgramming LanguagesSoftware Development

Kotlin is a modern alternative to Java that supports functional programming in the JVM. Here's a first look at programming with Kotlin using some of the concepts and syntax you already know from Java.

Finger selecting the word "Kotlin" on a virtual screen.
Credit: GagoDesign / Shutterstock

After Java, Kotlin is the most popular JVM language. Kotlin is an expressive, concise language with strong functional programming support. It’s especially appealing to Java developers because it is fully interoperable with Java, and the syntax is an easy transition. Let’s take a look at this dynamic programming language.

Get started with Kotlin

Kotlin is a great language for Java developers to expand into. It complements and embellishes what you already can do with Java and offers serious power from within the JVM ecosystem. Best of all, learning and using Kotlin doesn’t demand much from your already overworked brain, as switching between Java and Kotlin is fairly simple.

Like Java, Kotlin requires that you have a JDK installed. The command-line tool SDKMan makes installing and managing Kotlin simple:


$ sdk install kotlin 2.0.20
$ kotlin -version
Kotlin version 2.0.20-release-327 (JRE 23-ea+24-1995)

Once installed, you can create and run a simple Main.kt file:


// Main.kt
fun main() {
  println("Hello, InfoWorld!")
}

To compile it, enter:


$ kotlinc Main.kt

This command outputs a class file: MainKt.class, which you can run just like any other:


$ java MainKt
Hello, Kotlin!

Notice that a function with no return value, like the one above, doesn’t declare a void return value like in Java. Instead, it simply has no return modifier at all. Unlike Java, you can declare a function with the fun keyword outside of a class. In simple cases, functions lack all the trappings we’d find in Java: no package, class name, or public static void qualifiers.  Kotlin has all these capabilities, but it hides them by default, using conventions to provide a simpler syntax up front.

The basic idea in this section was to show how easy it is to write your code in Kotlin, which is like a streamlined and expanded Java, and run it within the JVM. It’s not that common to run Kotlin directly with Java tooling, but the example makes clear the relationship between Kotlin and Java. Usually, we’d run Kotlin using the kotlin runtime or a build tool like Gradle, which you’ll see in a moment.

First-class functions in Kotlin

Kotlin is a first-class functional language. It lets you pass around and return functional references from other functions, which provides enormous flexibility.

It is also often valid to write Java code right beside Kotlin:


// Main.kt
fun main() {
    System.out.println("Hello from Java, InfoWorld!");
    println("Hello, InfoWorld!")
}

On the other hand, some differences in Kotlin’s syntax make standard Java invalid. For one thing, Kotlin does not have or allow primitive types. In this respect, it is even more Java-like than Java: everything in Kotlin really is an object. There are no int, long, double, or char exceptions. (Project Valhalla is moving Java in a similar direction.)

Kotlin also makes a strong distinction between mutable and immutable variables. This is a common trend in modern languages. Any time you can live with an immutable version, you reduce the complexity of the program. In Kotlin, val means an immutable variable, while var means mutable:


val myValInt: Int = 10;
var myVarInt: Int = 10
// myValInt++; 

You may have also noticed by now that semicolons are optional in Kotlin, as they are in JavaScript. However, the common practice in Kotlin is to avoid using terminating semicolons.

Kotlin infers types like so:


val myString = "FooBar";
println("My string ${myString} is a classic.");

The above snippet also demonstrates Kotlin’s built-in String interpolation support, which has a similar syntax to many templating tools. The dollar-sign curly-brace (${}) can also contain expressions, as in: ${myString.upperCase()}. If the variable name is one without special characters, you can use a simplified form of it:


println("When in doubt, $myString.");

Beneath everything is Java’s type system, which you can access by typing:


println(myString::class.java.typeName); // Outputs “String”

Nulls

One of Kotlin’s embellishments is its more explicit handling of nulls. The NullPointerException is one of the most familiar exceptions in Java. In Kotlin, variables are non-nullable by default. The compiler will not allow you to set a null on normal variables. If you want a nullable version, you can set it up like so:


val myNullableString: String? = null

Kotlin also has the .? and :? operators to help deal with nulls. The .? is similar to JavaScript’s recently added optional chaining operator, and lets you short-circuit on null values without verbose checks:


possiblyNull.?possiblyNullMember.?anotherMember

If any of the parts are null, the whole expression returns null, without error. The nullish coalescing operator (?:—also called Elvis operator because it’s like an emoji with Elvis hair) lets you test the left side for null and return it if its non-null. Otherwise, it’ll return the right side, which may also be null:


something :? somethingElse

If something is null, you’ll get somethingElse.

Collections

Of course, you need to be able to handle collections of variables, and Kotlin has all the sets, lists, and maps you’d expect. These also allow for both mutable and immutable variants. So if you want a mutable list of strings you can enter something like:


import kotlin.collections.*;
fun main() {
    val books: MutableList = mutableListOf("Autobiography of a Yogi", "Slaughterhouse Five", "Phaedrus");
    println(books[2]);
}

Notice we imported the collections library in this snippet. Since that library exists in the Kotlin standard library, we can compile it:


$ kotlinc Main.kt

But it won’t run. Instead, we get


$ java MainKt
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/collections/CollectionsKt
        at MainKt.main(Main.kt:14)
        at MainKt.main(Main.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.collections.CollectionsKt
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528)

This is a familiar Java error telling us a class definition for CollectionsKt can’t be found. To get around the error, we need to include the standard library during runtime, like so:


$ java -cp /home/matthewcarltyson/kotlin:/home/matthewcarltyson/.sdkman/candidates/kotlin/current/lib/kotlin-stdlib.jar MainKt
Phaedrus

This command tells Java to include the kotlin-stdlib.jar (though yours might be be in a different location). Another approach is to use the kotlin runtime, which automatically includes stdlib for you:


$ kotlin MainKt

Kotlin also gives you strong functional programming support. For example, to check our books collection for a book we want, we’d enter:


println("Phaedrus" in books)  // outputs true

Also note that we can access lists just like arrays, using brackets:


println(books[2]) // outputs “Phaedrus”

Using Gradle with Kotlin

Gradle is often used as a build tool in Kotlin because you can use the Kotlin DSL instead of Groovy.

The easiest way to use Gradle and Kotlin together is to start a fresh project with Gradle init. That command launches an interactive questionnaire from the command line, shown with my answers here:


$ gradle init
Select type of project to generate:  2: application
Select implementation language: 4: Kotlin
Split functionality across multiple subprojects?:  1: no - only one application project
Select build script DSL:  2: Kotlin
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]: no  
Project name (default: kotlin): kotlin
Source package (default: kotlin): com.infoworld

My selections results in a Kotlin application using Kotlin as the build script language. Now, you can run this simple program from the project root with:


$ ./gradlew run

When you run the program, you’ll get a “Hello, World” greeting. Here’s how the project is laid out:


/settings.gradle.kts - Global settings for Gradle
/app/ build.gradle.kts - The build file
/app/src/main/kotlin/com/infoworld/App.kt - The single source file

Looking at the App.kt file, you’ll see:


package com.infoworld

class App {
    val greeting: String
        get() {
            return "Hello World!"
        }
}

fun main() {
    println(App().greeting)
}

There are some features here that you have not yet seen, including the use of a package declaration (in Kotlin, the package and directory structure don’t strictly have to match).

You can also see Kotlin’s streamlined syntax for declaring a class. In Kotlin, thedefault visibility of a class is public, so App is a public class. Inside of it, there is a read-only String member called greeting (remember that val declares a read-only variable). Notice that the greeting property declares a get() function. This is what will be executed when we use the dot operator to access it, something like a streamlined getter.

Now let’s attempt something more ambitious and download the character data for Chewbacca from the Star Wars API. We can modify the App.kt file like so:


//app/src/main/kotlin/com/infoworld/App.kt 
package com.infoworld

import com.google.gson.Gson
import java.net.URL

class App {
    fun fetchAndPrintCharacterInfo(url: String) {
        val gson = Gson()
        val response = URL(url).readText()
        val character = gson.fromJson(response, StarWarsCharacter::class.java)

        println("Name: ${character.name}")
        println("Height: ${character.height}")
    }
}

data class StarWarsCharacter(
    val name: String,
    val height: String,
)

fun main() {
    val chewbaccaUrl = "https://swapi.dev/api/people/13/"
    val app = App()
    app.fetchAndPrintCharacterInfo(chewbaccaUrl)
}

This gives us a look at Kotlin’s data class, which is designed for holding information (something akin to Java’s value objects). The StarWarsCharacter class has all the standard methods like getters and setters, hashCode, toString, and equals. It is ideal for unpacking API data into a container, which is what we’re doing here.

Add the following dependency to the dependencies section of /app/build.gradle.kts:


implementation("com.google.code.gson:gson:2.9.1")

This lets us handle the JSON we’ll get back from the API. Now, if we run the app we’ll see some information about Chewbacca:


$ ./gradlew run

> Task :app:run
Name: Chewbacca
Height: 228
Hair color: null
Eye color: null

BUILD SUCCESSFUL in 2s

Conclusion

The beauty of Kotlin for Java developers is that it fits fairly easily into your existing mental model. Kotlin lets you program inside the JVM, with all its intensive optimization and vast ecosystem, but with a functional language that is in some ways “more Java than Java,” yet easy to grasp and powerful. You can also use Kotlin alongside Java, so you don’t have to choose one or the other.

There’s a lot more to Kotlin, including an extension function (which lets you add class functions without subclasses), coroutines, more functional capabilities, lack of checked exceptions, and a simplified approach to object-oriented programming. We’ll continue exploring Kotlin and its features in my next article.