Lab · Playground
Kotlin playground
Edit Kotlin here and JetBrains' Kotlin Playground compiles and runs it — data classes, null safety, sealed types with when, extension functions, sequences, and coroutines.
Kotlin is JetBrains' answer to "Java, but pleasant": a statically typed language for the JVM,
announced in 2011, that keeps full Java interop while removing the boilerplate — data classes
instead of hand-written equals/hashCode, type inference, and a type system that tracks which
values can be null. Google made it the preferred language for Android in 2019, and it now
compiles to JavaScript and native code too.
The Kotlin compiler is far too big to run in a browser, so these blocks send your code to
JetBrains' Kotlin Playground service (api.kotlinlang.org), which compiles it with the latest
stable Kotlin, runs it on the JVM, and sends back the output. That is the only thing that leaves
your browser — the same arrangement as the Rust and Go
playgrounds. Each block is a complete program with a main function.
data class Point(val x: Int, val y: Int) {
operator fun plus(o: Point) = Point(x + o.x, y + o.y)
fun manhattan() = Math.abs(x) + Math.abs(y)
}
data class Robot(val name: String, val at: Point = Point(0, 0), val battery: Int = 100)
fun main() {
val r1 = Robot("R2")
val r2 = r1.copy(at = r1.at + Point(3, -4), battery = 92) // a changed copy; r1 is untouched
println(r1)
println(r2)
println("equal by value: ${Robot("R2") == r1}")
val (name, at, battery) = r2 // destructuring
println("$name is ${at.manhattan()} blocks from home with $battery% left")
val fleet = setOf(r1, r2, Robot("R2")) // hashCode is generated too
println("fleet size: ${fleet.size}")
}
data class User(val name: String, val email: String?, val manager: User?)
fun domainOf(u: User): String =
u.email?.substringAfter('@') ?: "(no email)" // ?. stops at null; ?: supplies a default
fun main() {
val boss = User("Alice", "alice@example.com", null)
val dev = User("Bob", null, boss)
println(domainOf(boss))
println(domainOf(dev))
println("Bob's manager's email: ${dev.manager?.email}")
println("Alice's manager's email: ${boss.manager?.email}")
val length: Int = dev.email?.length ?: 0
println("length $length")
// Smart casts: after the check, the compiler knows email is a String
for (u in listOf(boss, dev)) {
if (u.email != null) println("${u.name}: ${u.email.uppercase()}")
else println("${u.name}: no email to shout")
}
// val oops: String = dev.email // won't compile: String? is not String
}
sealed interface Shape
data class Circle(val r: Double) : Shape
data class Rect(val w: Double, val h: Double) : Shape
data class Triangle(val a: Double, val b: Double, val c: Double) : Shape
fun area(s: Shape): Double = when (s) { // no else: the compiler checks every case
is Circle -> Math.PI * s.r * s.r // s is smart-cast to Circle here
is Rect -> s.w * s.h
is Triangle -> {
val p = (s.a + s.b + s.c) / 2
Math.sqrt(p * (p - s.a) * (p - s.b) * (p - s.c))
}
}
fun main() {
val shapes = listOf(Circle(1.0), Rect(2.0, 3.5), Triangle(3.0, 4.0, 5.0))
for (s in shapes) println("%-40s %8.3f".format(s, area(s)))
println("largest: ${shapes.maxBy(::area)}")
}
// Add methods to types you don't own
fun String.isPalindrome(): Boolean {
val letters = filter(Char::isLetter).lowercase()
return letters == letters.reversed()
}
val Int.isEven get() = this % 2 == 0
data class Sale(val region: String, val item: String, val amount: Int)
fun main() {
println(listOf("Racecar", "A man, a plan, a canal: Panama", "Kotlin").map { it to it.isPalindrome() })
println((1..10).filter { it.isEven })
val sales = listOf(
Sale("north", "widgets", 1200), Sale("south", "widgets", 800),
Sale("north", "gadgets", 450), Sale("east", "gizmos", 1500), Sale("south", "gadgets", 975),
)
println(sales.groupBy { it.region }.mapValues { (_, v) -> v.sumOf { it.amount } })
println(sales.sortedByDescending { it.amount }.take(2).map { "${it.region}/${it.item}" })
val (big, small) = sales.partition { it.amount >= 1000 }
println("${big.size} big, ${small.size} small; items: ${sales.map { it.item }.distinct().sorted()}")
println(sales.associate { it.item to it.region }.entries.joinToString())
}
fun collatz(start: Long) = generateSequence(start) { n ->
if (n == 1L) null else if (n % 2 == 0L) n / 2 else 3 * n + 1
}
val primes = sequence { // a generator: yield suspends until the next value is wanted
val found = mutableListOf<Int>()
var n = 2
while (true) {
if (found.none { it * it <= n && n % it == 0 }) { found += n; yield(n) }
n++
}
}
fun main() {
println(collatz(27).count())
println(collatz(27).max())
println(primes.take(15).toList())
println(primes.dropWhile { it < 1000 }.first())
// Laziness: only as much work as the answer needs
var evaluated = 0
val firstBig = (1..1_000_000).asSequence().map { evaluated++; it * it }.first { it > 50 }
println("first square over 50 is $firstBig after $evaluated evaluations")
}
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
suspend fun fetch(name: String, ms: Long): String {
delay(ms) // suspends without blocking a thread
return "$name (${ms} ms)"
}
fun main() = runBlocking {
val time = measureTimeMillis {
val a = async { fetch("prices", 300) }
val b = async { fetch("stock", 200) }
val c = async { fetch("reviews", 250) }
println(listOf(a, b, c).awaitAll())
}
println("three 'requests' in about ${time / 100 * 100} ms, not 750")
// 10,000 coroutines are cheap; 10,000 threads would not be
val total = (1..10_000).map { i -> async { delay(10); i } }.awaitAll().sum()
println("sum from 10,000 coroutines: $total")
}