If you know Scala, you know Kotlin

HKT HKT
Views
Article hero image

Kotlin shares many similarities with Scala1, including its concise syntax, strong type system, and support for functional programming paradigm. Both languages are designed to be interoperable with Java, making them suitable for building large-scale applications.

While this post does not cover all aspects of Kotlin and Scala, it provides an overview of their similarities. So, if you know Scala, it is easy to learn Kotlin; or vice versa. This post also briefly touches on the features available only in Kotlin.

Main

Kotlin

fun main(args: Array<String>) {
  println("Hello, Kotlin!")
}

Scala

def main(args: Array[String]): Unit = {
  println("Hello, Scala!")
}

Variables

Kotlin

val name: String = "Rob" // val for immutable variable
var count = 30 // Type inferred, var for mutable variable

Scala

val name: String = "Rob"
var age = 30 // Type inferred

Conditionals

Kotlin

if (age >= 18) "Adult" else "Minor"

Scala

if age >= 18 then "Adult" else "Minor"

String Interpolation

Kotlin

val name = "Rob"
val age = 30
println("Hello, $name! You are $age years old.") // No s prefix needed for interpolation!

Scala

val name = "Rob"
val age = 30
println(s"Hello, $name! You are $age years old.")

Loops

Kotlin

for (i in 1..5) println(i) // 1 and 5 inclusive

for (i in 1..<5) println(i) // 1 to 4, or 1 until 5

for (i in 1..5 step 2) println(i)

var i = 1
while (i <= 5) {
  println(i)
  i += 1
}

Scala

for (i <- 1 to 5) println(i)

for (i <- 1 until 5) println(i)

for (i <- 1 to 5 by 2) println(i)

var i = 1
while i <= 5 do
  println(i)
  i += 1

Functions

Kotlin

fun greet(name: String): Unit {
  println("Hello, $name!")
}

fun add(a: Int, b: Int): Int  = a + b

fun factorial(n: Int): Int {
  tailrec fun helper(n: Int, acc: Int): Int =
      if (n <= 1) acc else helper(n - 1, n * acc)

  return helper(n, 1)
}

Scala

def greet(name: String): Unit = {
  println(s"Hello, $name!")
}

def add(a: Int, b: Int): Int = a + b

def factorial(n: Int): Int =
    @tailrec
    def helper(n: Int, acc: Int): Int =
      if (n <= 1) acc else helper(n - 1, n * acc)

    helper(n, 1)

Collections

Kotlin

// Immutable collections
val arr = arrayOf(1, 2, 3, 4, 5)
val list = listOf(1, 2, 3, 4, 5)
val set = setOf(1, 2, 3, 4, 5)
val map = mapOf(1 to "one", 2 to "two", 3 to "three")


// access items by index
arr(1)
list.get(4) // or list[4]

// access element by key in a map
val value: String? = map.get(1)
val value = map[1] // throws error if key not found

// iterating elements
for (e in arr) { // or list or set
  println(e)
}

for ((k, v) in map) {
  println("$k -> $v")
}

// Mutable Collections
val ma = mutableArrayOf(1, 2, 3, 4, 5)
val ml = mutableListOf(1, 2, 3, 4, 5)
val ms = mutableSetOf(1, 2, 3, 4, 5)
val mm = mutableMapOf(1 to "one", 2 to "two", 3 to "three")

Scala

// Immutable collections
val arr = Array(1, 2, 3, 4, 5)
val list = List(1, 2, 3, 4, 5)
val set = Set(1, 2, 3, 4, 5)
val map = Map(1 -> "one", 2 -> "two", 3 -> "three")

// access items by index
arr(1)
list(1)

// access element by key in a map
val value: Option[String] = map.get(1)

// iterating elements
for (e <- arr) { // or list or set
  println(e)
}

for ((k, v) <- map) {
  println("$k -> $v")
}

import scala.collection.mutable

val ma = mutable.ArrayBuffer(1, 2, 3, 4, 5)
val ml = mutable.ListBuffer(1, 2, 3, 4, 5)
val ms = mutable.Set(1, 2, 3, 4, 5)
val mm = mutable.Map(1 -> "one", 2 -> "two", 3 -> "three")

Classes

Kotlin

class Person(val name: String, val age: Int) { // Primary constructor
  lateinit var address: String

  init {
    if (!this::address.isInitialized) {
      address = "123 Main St"
    }
  }
  constructor(name: String) : this(name, 0) // Secondary constructor with default age

  // Auxiliary constructor
  constructor(name: String, age: Int, city: String) : this(name, age) {
    println("Name = $name, Age = $age, City = $city")
  }
}

// elsewhere
val tom = Person("Tom", 23)

Scala

class Person(val name: String, val age: Int) { // Primary constructor
  lazy val address = "123 Main St"

  def this(name: String) = // Auxiliary constructor
    this(name, 0) // Calls the primary constructor with a default age of 0

  def this(name: String, age: Int, city: String) = // Auxiliary constructor
    this(name, age) // Calls the primary constructor
    println(s"Name = $name, Age = $age, City = $city")
}

// elsewhere
val tom = Person("Tom", 23)

In Kotlin, casses are closed for inheritance by default. Use open modifier on the the class to be able to derive from a class.

Kotlin

open class Vehicle(val make: String) {
  open fun drive() {
    println("Driving a $make vehicle")
  }
}

class Car(make: String) : Vehicle(make) {
  override fun drive() {
    println("Driving a $make car")
  }
}

In Scala, classes are extensible by default. Use the final modifier on the class to restrict extending it.

Scala

class Vehicle(val make: String) {
  def drive(): Unit =
    println(s"Driving a $make vehicle")
}

final class Car(make: String) extends Vehicle(make) {
  override def drive(): Unit =
    println(s"Driving a $make car")
}

Companion Objects

Kotlin

object Singleton { // standalone singleton object
  fun greet(name: String) {
    println("Hello, $name!")
  }
}

class Person(...) {
  companion object Person {
    fun create(name: String): Person = Person(name, 24, "New York")
  }
}

Scala

object Singleton: // standalone singleton object
  def greet(name: String): Unit =
    println("Hello, $name!")

class Person(...)
object Person:
  def apply(name: String): Person = new Person(name, 24, "New York")

Data Classes

Kotlin

data class Person(val name: String, val age: Int)

Scala

case class Person(val name: String, val age: Int)

Sealed Hierarchies and ADTs

Kotlin

sealed interface Shape
data class Circle(val radius: Double) : Shape()
data class Rectangle(val width: Double, val height: Double) : Shape()

fun area(shape: Shape): Double =
  when (shape) {
    is Shape.Circle -> Math.PI * shape.radius * shape.radius
    is Shape.Rectangle -> shape.width * shape.height
  }

Scala

sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape

def area(shape: Shape): Double =
  shape match
    case Circle(radius) => Math.PI * radius * radius
    case Rectangle(width, height) => width * height

Option / Nullable

Kotlin

val maybeName: String? = null // maybeName is nullable
val name: String = maybeName ?: "Unknown"

Scala

val nameOpt = Option(null)
val name = nameOpt.getOrElse("Unknown")

Pattern Matching

Kotlin

// Conditions in cases
val age = 30
val status =
  when {
    age >= 18 -> "Adult"
    age >= 13 -> "Teenager"
    else -> "Child"
  }

// Case collapsing
fun isWeekend(day: String): Boolean =
  when (day) {
    "Saturday", "Sunday" -> true
    else -> false
  }

// Checking for types
fun check(e: Exception) =
  when(e) {
    is IllegalArgumentException -> "Invalid argument"
    is IllegalStateException    -> "Illegal state"
    else                        -> "Unknown error"
  }

Scala

// Conditions in cases
val age = 30
val status =
  age match
    case age if age >= 18 => "Adult"
    case age if age >= 13 => "Teenager"
    case _ => "Child"
}

// Case collapsing
def isWeekend(day: String): Boolean =
  day match
    case "Saturday" | "Sunday" => true
    case _ => false

// Checking for types
def check(e: Exception): String =
  e match
    case _: IllegalArgumentException => "Invalid argument"
    case _: IllegalStateException    => "Illegal state"
    case _                           => "Unknown error"

Extension Methods

Kotlin

operator fun String.titleCase(s: String): String =
  s.split(" ").map { it.capitalize() }.joinToString(" ") // it is similar to _ in Scala

Scala

extension (st: String.type) {
  def titleCase(s: String): String =
    st.split(" ").map(_.capitalize).mkString(" ")
}

etc

Name Kotlin Scala
Shift operators shl, shlr <<, >>
Bitwise operators or, and |, &

Only in Kotlin

Top level constants

const val PI = 3.14159

def main(args: Array<String>) {
    println("Hello, Kotlin!")
}

do-while Loop

var i = 10
do {
  println(i)
  i -= 1
} while (i >= 1)

Getters and Setters

class Person(val name: String, val age: Int, val city: String) {
  var address: String = ""
    get() = field
    set(value) {
      field = value.uppercase()
    }
}

// elsewhere
val person = Person("John", 24, "New York")
println(person.address) // ""
person.address = "123 Main St"
println(person.address) // "123 MAIN ST"

  1. Version of Scala used in this post is v3.x with braceless syntax. ↩︎

scala kotlin