Matthew Tyson
Contributing Writer

Kotlin for Java developers: Getting started

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.

Matthew Tyson
Contributing Writer

Matthew Tyson is a contributing writer at InfoWorld. A seasoned technology journalist and expert in enterprise software development, Matthew has written about programming, programming languages, language frameworks, application platforms, development tools, databases, cryptography, information security, cloud computing, and emerging technologies such as blockchain and machine learning for more than 15 years. His work has appeared in leading publications including InfoWorld, CIO, CSO Online, and IBM developerWorks. Matthew also has had the privilege of interviewing many tech luminaries including Brendan Eich, Grady Booch, Guillermo Rauch, and Martin Hellman.

Matthewโ€™s diverse background encompasses full-stack development (Java, JVM languages such as Kotlin, JavaScript, Python, .NET), front-end development (Angular, React, Vue, Svelte) and back-end development (Spring Boot, Node.js, Django), software architecture, and IT infrastructure at companies ranging from startups to Fortune 500 enterprises. He is a trusted authority in critical technology areas such as database design (SQL and NoSQL), AI-assisted coding, agentic AI, open-source initiatives, enterprise integration, and cloud platforms, providing insightful analysis and practical guidance rooted in real-world experience.

More from this author