CS 346 (W23)
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Getting Started

Installing Kotlin

You need the Kotlin compiler and runtime. We’ll run on the Java JVM. You can either install the command-line tools, or install IntelliJ IDEA and run everything from within the IDE (which is recommended).

Compiling Code

Compiled languages require an explicit step to compile code and generate native executables. This is done ahead of time, and the executables are distributed to users. e.g. C++

  • The compilation cost is incurred before the user runs the program, so we get optimal startup performance.
  • The target system architecture must be known ahead of time, since we’re distributing native binaries.

Interpreted languages allow developers to distribute the raw source code which can be interpreted when the user executes it.

  • This requires some ‘runtime engine‘ that can convert source code to machine code on-the-fly. Results of this operation can often be cached so that the compilation cost is only incurred when it first executes.

compilers_interpreters

Some languages can be compiled to a secondary format (IR, ”intermediate representation”) and then interpreted. Languages running on the Java Virtual Machine (JVM) are compiled ahead of time to IR, and then interpreted at runtime.

Kotlinc compiled to JVM

Kotlin can be compiled or interpreted!

  • Kotlin/JVM compiles Kotlin code to JVM bytecode, which can run on any Java virtual machine.
  • Kotlin/Android compiles Kotlin code to native Android binaries, which leverage native versions of the Java Library and Kotlin standard libraries.
  • Kotlin/Native compiles Kotlin code to native binaries, which can run without a virtual machine. It is an LLVM based backend for the Kotlin compiler and native implementation of the Kotlin standard library.
  • Kotlin/JS transpiles (converts) Kotlin to JavaScript. The current implementation targets ECMAScript 5.1 (with plans to eventually target ECMAScript 2015).

There are three primary ways of executing Kotlin code:

  1. Read-Evaluate-Print-Loop (REPL): Interact directly with the Kotlin runtime, one line at-a-time. In this environment, it acts like a dynamic language.
  2. KotlinScript: Use Kotlin as a scripting language, by placing our code in a script and executing directly from our shell. The code is compiled automatically when we execute it, which eliminates the need to compile ahead-of-time.
  3. Application: We can compile standalone applications, targetting native or JVM [ed. we will use JVM in this course].

REPL

REPL is a paradigm where you type and submit expressions to the compiler one line-at-a-time. It’s commonly used with dynamic languages for debugging, or checking short expressions. It’s not intended as a means of writing full applications!

> kotlin
Welcome to Kotlin version 1.6.10 (JRE 17.0.2+8-86)
Type :help for help, :quit for quit
>>> val message="Hello Kotlin!"
>>> println(message)
Hello Kotlin!

KotlinScript

KotlinScript is Kotlin code in a script file that we can execute from our shell. This makes Kotlin an interesting alternative to a language like Python for shell scripting.

> cat hello.kts 
#!/usr/bin/env kotlin
val message="Hello Kotlin!"
println(message)

> ./hello.kts 
Hello Kotlin!

Kotlin compiles scripts in the background before executing them, so there’s a delay before it executes [ed. I fully expect that later versions of Kotlin will allow caching the compilation results to speedup script execution time].

This is a great way to test functionality, but not a straight-up replacement for shell scripts, due to the runtime costs1.

Applications

Kotlin applications are fully-functional, and can be compiled to native code, or to the JVM. Kotlin application code looks a little like C, or Java. Here’s the world’s simplest Kotlin program, consisting of a single main method2.

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!

Using Libraries

Kotlin has full access to it’s own class libraries, plus any others that are imported and made available. Kotlin is 100% compatible with Java libraries, and makes extensive use of Java libraries when possible. For example, Kotlin collection classes actually use some of the underlying Java collection libraries!

In this section, we’ll discuss how to use existing libraries in your code. We need to talk about namespaces and qualifying classes before we can talk about libraries.

Packages

The package keyword is used to assign a file to a namespace. To use a class from a different namespace, we need to import the related class by using the import keyword. This applies to any class that we wish to use. In the example below, we import our ErrorMessage class from the ca.uwaterloo.cs346 namespace so that we can use it.

import ca.uwaterloo.cs346.ErrorMessage

class Logger {
  val error = ErrorMessage()
  error.printMessage()
}

Type Aliases

Type aliases provide alternative names for existing types. If the type name is too long you can introduce a different shorter name and use the new one instead. This can make your code much more readable!

Note that type aliases do not introduce new types. They are equivalent to the corresponding underlying types, so there is no runtime overhead.

 typealias NodeSet = Set<Network.Node>
 typealias FileTable<K> = MutableMap<K, MutableList<File>>

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.

Using Java Libraries

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 refer to any of those classes in the current 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. However, to use any other Java or Kotlin library, you will need to take additional steps. We’ll discuss this when we cover build systems and Gradle.

  1. Scripts will be compiled and cached locally, but there’s still some runtime performance issues. ↩︎

  2. This chapter focuses mainly on the Kotlin language. In the next chapter, we’ll dive deeper into constructing applications. ↩︎