Data Types Workbook

Eleven short exercises on Kotlin's basic types — val and var, the number types, explicit conversions, strings and templates, nullability, and Unit and Nothing.

Practice problems for In Kotlin, There Are No Primitives. Each takes a minute or two. A couple of exercises auto-check: implement the function in the editor and press Run — hidden tests go green when you’re right and red (with a hint) when you’re not, all on JetBrains’ Kotlin compiler server. The rest stay attempt-then-reveal: fill in the TODO, press Run, then click Show answer to check yourself. Nothing here is a trick question, just direct practice of the syntax from the lesson.

val, var, and the number types

1. Read-only and mutable

Declare a read-only name holding "Ada" and a mutable age holding 36, then reassign age to 37.

Try it — edit, then press Run fun main() { // TODO: read-only name = "Ada", mutable age = 36, then reassign age to 37 val name = "" val age = 0 println(name + " " + age) }
Show answer Hide answer
val name = "Ada"
var age = 36
age = 37

2. Literals pick their type

Write literals for: a Long equal to 42, a Double equal to 3.14, and a Float equal to 3.14.

Try it — edit, then press Run fun main() { // TODO: write a Long = 42, a Double = 3.14, and a Float = 3.14 val l = 0 val d = 0.0 val f = 0.0 println(l.toString() + " " + d + " " + f) }
Show answer Hide answer
val l = 42L
val d = 3.14
val f = 3.14f

3. A readable big number

Write the literal 8100000000 as a Long with digit separators for readability.

Try it — edit, then press Run fun main() { // TODO: write 8100000000 as a Long with digit separators for readability val population = 0L println(population) }
Show answer Hide answer
val population = 8_100_000_000L

4. No implicit widening

This doesn’t compile: val l: Long = anInt. Fix it without changing anInt’s type.

Try it — edit, then press Run fun main() { val anInt = 42 // TODO: assign anInt to a Long without changing anInt's type val l: Long = 0L println(l) }
Show answer Hide answer
val l: Long = anInt.toLong()

Kotlin never widens numeric types silently — you convert explicitly.

Char, strings, and templates

5. A character’s code

Implement charCode so it returns a character’s numeric code — for 'A', that’s 65.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun charCode() { Assert.assertEquals("'A' has code 65", 65, charCode('A')) Assert.assertEquals("'a' has code 97", 97, charCode('a')) } } //sampleStart fun charCode(c: Char): Int = 0 // TODO: return the character's numeric code //sampleEnd
Show answer Hide answer
fun charCode(c: Char) = c.code

Char is its own type, not a number, so you ask for the code rather than using it as one.

6. A string template

Given val age = 36, print Next year: 37 using a string template with an expression.

Try it — edit, then press Run fun main() { val age = 36 // TODO: print "Next year: 37" using a string template with an expression println("Next year: ?") }
Show answer Hide answer
println("Next year: ${age + 1}")

7. A raw string

Write the Windows path C:\Users\ada as a string literal without escaping the backslashes.

Try it — edit, then press Run fun main() { // TODO: write the path C:\Users\ada as a raw string, no escaping val path = "" println(path) }
Show answer Hide answer
val path = """C:\Users\ada"""

Nullability and the special types

8. Opt into null

Declare a String that may hold null and assign it null. Then declare one that may not hold null.

Try it — edit, then press Run fun main() { // TODO: declare a String? holding null, and a non-null String val maybe = "" val name = "" println(maybe) println(name) }
Show answer Hide answer
var maybe: String? = null
val name: String = "Ada"

9. Length or zero

Implement lengthOrZero so it returns the string’s length, or 0 when it’s null — in one expression.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun lengthOrZero() { Assert.assertEquals("length of a present string", 2, lengthOrZero("hi")) Assert.assertEquals("zero when null", 0, lengthOrZero(null)) } } //sampleStart fun lengthOrZero(maybe: String?): Int = 0 // TODO: maybe's length, or 0 when it's null //sampleEnd
Show answer Hide answer
fun lengthOrZero(maybe: String?) = maybe?.length ?: 0

10. A function that never returns

Write fail(message: String) that always throws IllegalStateException. What’s its return type?

Try it — edit, then press Run // TODO: make fail always throw IllegalStateException. What's its return type? fun fail(message: String) { println(message) } fun main() { fail("nope") }
Show answer Hide answer
fun fail(message: String): Nothing = throw IllegalStateException(message)

Nothing — the type of an expression that never returns a value.

11. The void stand-in

A function prints a line and returns no meaningful value. What is its return type, and do you have to write it?

Try it — edit, then press Run // TODO: write log(msg: String) that prints msg. What's its return type — and do you write it? fun log(msg: String) { // TODO: print msg } fun main() { log("hello") println("done") }
Show answer Hide answer

The return type is Unit, and you don’t write it — it’s the default when you omit the return type.

fun log(msg: String) {
    println(msg)
}

Going deeper: overflow, unsigned, and boxing

12. Silent overflow

Kotlin’s fixed-width integers wrap on overflow, exactly like Java. Make overflow() return whatever Int.MAX_VALUE + 1 evaluates to, and predict the sign before you run it.

Try it — edit, then press Run fun main() { // TODO: return Int.MAX_VALUE + 1 — what sign is it? println(overflow()) } fun overflow(): Int = 0
Show answer Hide answer
fun overflow(): Int = Int.MAX_VALUE + 1   // -2147483648

No exception — it wraps around to the most negative Int. If a sum might exceed Int.MAX_VALUE, use Long or the checked Math.addExact.

13. Reinterpret the bits as unsigned

Implement asUnsigned so it returns the unsigned interpretation of an Int’s bit pattern — for -1, that’s 4294967295.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun asUnsigned() { Assert.assertEquals(4294967295u, asUnsigned(-1)) Assert.assertEquals(0u, asUnsigned(0)) } } //sampleStart fun asUnsigned(n: Int): UInt = 0u // TODO: reinterpret n's bits as an unsigned value //sampleEnd
Show answer Hide answer
fun asUnsigned(n: Int) = n.toUInt()

toUInt() reinterprets the same 32 bits without sign, so -1 (all ones) becomes 4294967295.

14. Walk the alphabet

Char + Int gives a Char, but Char + Char doesn’t compile. Implement shift so shift('A', 1) is 'B'.

Implement it, then press Run to check import org.junit.Test import org.junit.Assert class Test { @Test fun shift() { Assert.assertEquals('B', shift('A', 1)) Assert.assertEquals('a', shift('z', -25)) } } //sampleStart fun shift(c: Char, n: Int): Char = ' ' // TODO: return the character n places along //sampleEnd
Show answer Hide answer
fun shift(c: Char, n: Int) = c + n

Char + Int is defined and returns a Char; c.code would give you the number the other way.

15. Structural vs referential

Given two strings built differently but with the same contents, which comparison is truea == b or a === b? Reveal to check your reasoning.

Show answer Hide answer

a == b is true (same contents — == calls equals), while a === b is false (different objects). And for boxed numbers, === on two Int? is now a compiler warning, precisely because the answer depends on the boxing cache.


Back to the lesson, In Kotlin, There Are No Primitives, or on to the next one: functions.

Comments