Introduction
Why Kotlin?
Kotlin is a modern, general-purpose programming language designed by JetBrains as a drop-in replacement for Java. It has full compatibility with Java source code, and is 100% compatible with that ecosystem.
Over time, it has extended far past it’s original goals. It’s seen broad industry support, and has proven to be a versatile language for full-stack development.
- As of 2019, it’s Google’s recommended language for building Android applications. Android development, including third-party libraries, is typically done in Kotlin1.
- It’s fully capable back-end language for building servers and services, and it’s compatibile with existing service frameworks e.g., Spring Boot, Ktor.
- Finally, it’s designed as a cross-platform language, with native compilers for many platforms . We use it in this course for desktop and mobile development, and other targets e.g.,
wasm
are under active development.

Compiling
The Kotlin programming language includes multiple compilers that can be used to target different platforms, using the same code.
flowchart LR kotlin([Kotlin Code]) jvm([Kotlin/JVM]) native([Kotlin/Native]) js([Kotlin/JS]) wasm([Kotlin/WASM]) kotlin --> jvm kotlin --> native kotlin --> js kotlin --> wasm
Kotlin/JVM
compiles Kotlin to JVM bytecode, which can be interpreted on a Java virtual machine. This supports running Kotlin code anywhere a JVM is supported (typically desktop, server).Kotlin/Native
compiles Kotlin to native executables. This provides support for ARM, Android, iOS and other targets.Kotlin/JS
transpiles Kotlin to JavaScript. The current implementation targets ECMAScript 5.1.Kotlin/WASM
adds support for WASM virtual machine standard, allowing Kotlin to run on the web.
Kotlin’s application code looks a little like C, or Java. Here’s the world’s simplest Kotlin program, consisting of a single main method.
fun main() {
val message="Hello Kotlin!"
println(message)
}
To compile from the command-line, we can use the Kotlin compiler, kotlinc
. By default, it takes Kotlin source files (.kt
) and compiles them into corresponding class files (.class
) that can be executed on the JVM.
$ kotlinc Hello.kt
$ ls
Hello.kt HelloKt.class
$ kotlin HelloKt
Hello Kotlin!
Notice that the compiled class is named slightly differently than the source file. If your code isn’t contained in a class, Kotlin wraps it in an artificial class so that the JVM (which requires a class) can load it properly. Later when we use classes, this won’t be necessary.
This example compiles Hello.kt
into Hello.jar
and then executes it:
$ kotlinc Hello.kt -include-runtime -d Hello.jar
$ ls
Hello.jar Hello.kt
$ java -jar Hello.jar
Hello Kotlin!
Most of the time, we won’t compile from the command-line; instead, we will use a build system to generate an installable package for our platform e.g., desktop or Android.
Modularity
Packages
When defining related classes or functions within an application, it’s best to group them together in a package.
Packages are meant to be a collection of related classes. e.g. graphics classes. They are conceptually similar to namepsaces in C++. We use them to enforce a clear separation of concerns within code.
Packages are named using a reverse DNS name, typically the reverse-domain name of company that owns the code, followed by a unique name related to the code’s functionality. e.g. com.sun.graphics
for a graphics package developed at Sun Microsystems, or ca.uwaterloo.cs346
for code related to this course.
To create and use a package, you need to:
- Add the
package
declaration to the top of a source file to assign that file to a namespace. Classes or modules in the same package have full visibility to each other.
For example, we have declared this file to be included in the cs.uwaterloo.cs346
package. This means that any classes in this file will be included in that package, and will be visible to any other functions or classes in that package, even if they are in different files.
package ca.uwaterloo.cs346
data class Point(x: Float, y: Float) { }
data class Vector(x: Float, y: Float, m: Float) { }
- Use the
import
keyword to bring classes from other packages into the current namespace. This allows you to use classes from other packages in your code.
For example, to use our Point
class in another file, we would import it like this. Note that the Vector
class isn’t imported, so it won’t be available in this file.
import ca.uwaterloo.cs346.Point
fun main() {
val p1 = Point(5.0, 10.0)
val p2 = Point(15.0, 20.0)
}
To include all classes in a package, you can use the *
wildcard. This would make both Point
and Vector
visible.
import ca.uwaterloo.cs346.*
Modules
Modules serve a different purpose than packages: they are intended to expose permissions for external dependencies. Using modules, you can create higher-level constructs (modules) and have higher-level permissions that describe how that module can be reused. Modules are intended to support a more rigorous separation of concerns that you can obtain with packages.
A simple application would be represented as a single module containing one or more packages, representing different parts of our application.
JAR Files
A Kotlin application might consist of many classes, each compiled into it’s own .class
file. To make these more manageable, the Java platform includes the jar
utility, which can be used to combine your classes into a single archive file i.e. a jar
file. Your application can be executed directly from that jar file. This is the standard mechanism in the Java ecosystem for distributing applications.
A jar
file is just a compressed file (like a zip
file) which has a specific structure and contents, and is created using the jar
utility. This example compiles Hello.kt
, and packages the output in a Hello.jar
file.
$ kotlinc Hello.kt -include-runtime -d Hello.jar
$ ls
Hello.jar Hello.kt
The -d
option tells the compiler to package all of the required classes into our jar file. The -include-runtime
flag tells the compiler to also include the Kotlin runtime classes (which contain things like the Garbage Collector).
These classes are needed for all Kotlin applications, and they’re small, so you should always include them in your distribution (if you fail to include them, you app won’t run unless your user has Kotlin installed).
To run from a jar file, use the java
command.
$ java -jar Hello.jar
Hello Kotlin!
In the same way that the Java compiler compiles Java code into IR code that will run on the JVM, the Kotlin compiler also compiles Kotlin code into compatible IR code. The java
command will execute any IR code, regardless of which programming language and compiler was used to produce it.
Our JAR file from above looks like this if you uncompress it:
$ unzip Hello.jar -d contents
Archive: Hello.jar
inflating: contents/META-INF/MANIFEST.MF
inflating: contents/HelloKt.class
inflating: contents/META-INF/main.kotlin_module
inflating: contents/kotlin/collections/ArraysUtilJVM.class
...
$ tree -L 2 contents/
.
├── META-INF
│ ├── MANIFEST.MF
│ ├── main.kotlin_module
│ └── versions
└── kotlin
├── ArrayIntrinsicsKt.class
├── BuilderInference.class
├── DeepRecursiveFunction.class
├── DeepRecursiveKt.class
├── DeepRecursiveScope.class
...
The JAR file contains these main features:
HelloKt.class
– a class wrapper generated by the compilerMETA-INF/MANIFEST.MF
– a file containing metadata.kotlin/
– Kotlin runtime classes not included in the JDK.
The MANIFEST.MF
file is autogenerated by the compiler, and included in the JAR file. It tells the runtime which main
method to execute. e.g. HelloKt.main()
.
$ cat contents/META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: JetBrains Kotlin
Main-Class: HelloKt
Library support
Kotlin benefits from some of the best library support in the industry, since it can leverage any existing Java libraries as well as Kotlin standard libraries. This means that you can use any Java library in your Kotlin code, and you can also use any Kotlin library that has been developed.
Kotlin Standard Library
The Kotlin Standard Library is included with the Kotlin language, and contained in the kotlin
package. This is automatically imported and does not need to be specified in an import statement.
Some of the features that will be discussed below are actually part of the standard library (and not part of the core language). This includes essential classes, such as:
- Higher-order scope functions that implement idiomatic patterns (let, apply, use, etc).
- Extension functions for collections (eager) and sequences (lazy).
- Various utilities for working with strings and char sequences.
- Extensions for JDK classes making it convenient to work with files, IO, and threading.
Java Standard Library
Kotlin is completely 100% interoperable with Java, so all of the classes available in Java/JVM can also be imported and used in Kotlin.
// import all classes in the java.io package
// this allows us to reference any classes in this namespace
import java.io.*
// we can also just import a single class
// this allows us to refer to just the ListView class in code
import javafx.scene.control.ListView
// Kotlin code calling Java IO libraries
import java.io.FileReader
import java.io.BufferedReader
import java.io.FileNotFoundException
import java.io.IOException
import java.io.FileWriter
import java.io.BufferedWriter
if (writer != null) {
writer.write(
row.toString() + delimiter +
s + row + delimiter +
pi + endl
)
Importing a class requires your compiler to locate the file containing these classes! The Kotlin Standard Library can always be referenced by the compiler, and as long as you’re compiling to the JVM, the Java class libraries will also be made available.
Third-Party Libraries
To use any other Java or Kotlin library, you will to add it to your Gradle dependencies in your build.gradle.kts
. This makes them available to import in your source code
// build.gradle.kts
dependencies {
implementation("com.github.ajalt.clikt:clikt:4.2.2")
}
Once the library is added to your dependencies, you can import it in your source code:
import com.github.ajalt.clikt.core.CliktCommand
fun process() {
val command = object : CliktCommand() {
override fun run() {
echo("Hello, world!")
}
}
command.main(arrayOf())
}
See Add Libraries & Plugins for more information on how to manage dependencies in Kotlin.
-
According to Google (Google I/O 2025), More than 60% of the top 1000 apps on the Google Play Store are written in Kotlin. ↩︎