2026-10-07 Kotlin Basics: Types, Functions, Unit, File Structure, if and when
Today I started learning Kotlin: basic types, how to write functions, Unit, file structure, and how to write if and when. While writing this up, I compiled and ran every example to check the results.
Environment: Kotlin 2.2.20 (the
kotlincbundled with IntelliJ IDEA) on JVM 21. The outputs and error messages below are what this environment actually produced.
1. Basic types and variables
val and var
val name = "Kotlin" // cannot be reassigned (similar to a final variable in Java)var count = 0 // can be reassignedcount += 1Use val by default, and var only when the value needs to change.
Type inference and basic types
You don't have to write the type; it is inferred from the assigned value. To write it explicitly, use name: Type.
val i = 42 // Intval l = 3_000_000_000 // exceeds the Int range, so Longval l2 = 10L // the L suffix makes it Longval d = 3.14 // Doubleval f = 2.5f // the f suffix makes it Floatval b = true // Booleanval c = 'A' // Charval s = "Kotlin" // Stringval explicit: Int = 42 // writing the type explicitlyPrinting the type name of each value gives:
Int Long Long Double Float Boolean Char StringYou can put _ between digits for readability, as in 3_000_000_000. Integer literals are Int by default, but are inferred as Long when they don't fit in Int.
Number types are not widened automatically
In Java you can assign an int to a long variable directly. In Kotlin you can't.
val i: Int = 1val l: Long = i // compile errorval ok: Long = i.toLong() // must convert explicitlyerror: initializer type mismatch: expected 'Long', actual 'Int'.You call conversion functions such as toLong(), toInt(), and toDouble() explicitly.
String templates
Inside a string, $variable or ${expression} inserts a value.
val a = 1val b = 2println("$a + $b = ${a + b}") // 1 + 2 = 32. Writing functions
Basic form
fun add(a: Int, b: Int): Int { return a + b}- Functions start with the
funkeyword. - Parameters are written
name: Type, the opposite order of Java (int a). - The return type goes after the parentheses as
: Type.
Expression body
If the body is a single expression, you can drop the braces and return and use =. The return type is then inferred too.
fun multiply(a: Int, b: Int) = a * bDefault values and named arguments
fun greet(name: String, greeting: String = "Hello"): String = "$greeting, $name!"greet("Kotlin") // Hello, Kotlin!greet(greeting = "Hi", name = "Kim") // Hi, Kim!Parameters can have default values, and naming arguments at the call site lets you pass them in any order. You don't need several overloads the way you would in Java.
3. Unit
A function that returns no meaningful value has the return type Unit. It plays the role of Java's void and can be omitted.
fun printSum(a: Int, b: Int) { // return type omitted = Unit println("$a + $b = ${a + b}")}fun printSumExplicit(a: Int, b: Int): Unit { // writing it is also fine println("$a + $b = ${a + b}")}The difference from void is that Unit is an actual value (an object). You can store the return value in a variable and print it.
val r = printSum(1, 2)println(r) // kotlin.Unitprintln(r == Unit) // true1 + 2 = 3kotlin.Unittruevoid means "no value", so it can't be stored in a variable. Unit is a type with exactly one value, so it can be handled like any other type.
4. File structure
package study.basicsimport kotlin.math.maxfun main() { println(max(3, 7))}packagegoes at the top, followed byimports.- Functions can live at the top level of a file, outside any class. Unlike Java, you don't need a class just to hold
main. - The entry point is
fun main(). If you don't need the arguments, you can leave outargs. - The file name doesn't have to match a class name, and one file can hold several classes and functions.
On the JVM, every function still has to live inside a class, so the compiler generates a FileNameKt class for top-level functions. Compiling Basics.kt produced BasicsKt.class, and that is the name used to run it.
$ kotlinc Basics.kt -d out$ ls out/study/basicsBasicsKt.class$ kotlin -cp out study.basics.BasicsKt5. if is an expression
In Kotlin, if is an expression that returns a value. That's why there is no separate ternary operator (a > b ? a : b).
fun maxOf2(a: Int, b: Int) = if (a > b) a else bWith a block, the value of the last expression becomes the result.
val result = if (count > 0) { println("positive block") "positive" // this becomes the value of result} else { "zero"}When used as an expression, else is required, because otherwise there would be no value when the condition is false.
val z = if (x > 0) "p" // compile errorerror: 'if' must have both main and 'else' branches when used as an expression.6. when
when replaces Java's switch. It needs no break, and it can also be used as an expression.
Values, multiple values, ranges
fun grade(score: Int): String = when (score) { 100 -> "Perfect" in 90..99 -> "A" // 90 through 99 in 80 until 90 -> "B" // 80 up to, but not including, 90 else -> "C or below"}fun dayType(day: String) = when (day) { "Sat", "Sun" -> "Weekend" // several values separated by commas else -> "Weekday"}.. includes the end, and until excludes it.
Type checks and smart casts
fun describe(x: Any): String = when (x) { 1 -> "The number 1" "hello" -> "A greeting" is Long -> "A Long" !is String -> "Not a string" else -> "Some other string: ${x.length}"}describe(1) → The number 1describe("hello") → A greetingdescribe(5L) → A Longdescribe(2.0) → Not a stringdescribe("abc") → Some other string: 3In the final else branch, x is declared as Any, yet x.length compiles. The earlier !is String branch already caught every non-string value, so the compiler treats x as a String here. This is called a smart cast, and no explicit cast is needed.
when without a subject
Without a subject in parentheses, each branch can be a condition. This replaces an if-else if chain.
fun sign(n: Int) = when { n < 0 -> "negative" n == 0 -> "zero" else -> "positive"}As an expression, every case must be covered
When when is used as an expression, it must cover every possible case, usually with an else.
val y = when (x) { 1 -> "one" } // compile errorerror: 'when' expression must be exhaustive. Add an 'else' branch.Summary
| Topic | What to remember |
|---|---|
| Variables | val by default, var when the value changes |
| Types | Mostly inferred. Numbers aren't widened automatically; convert with toLong() etc. |
| Functions | fun name(arg: Type): ReturnType; use = for a single expression |
| Arguments | Default values and named arguments reduce the need for overloads |
| Unit | Plays the role of void, but is a type with one real value. Can be omitted |
| Files | Top-level functions are allowed; on the JVM they compile into a FileNameKt class |
| if | An expression that returns a value. Replaces the ternary operator; else is required as an expression |
| when | Replaces switch. Supports multiple values, ranges, type checks; must be exhaustive as an expression |
Report an error or share feedback
Open a draft with this article’s title and URL. Review the message and recipient before sending.
Open email draftIf no email app opens, copy these details into your usual email service.
Related posts
Today I Learned 2026-10-06 Practical Claude Code Commands for Developers
The habits heavy Claude Code users keep recommending, and the commands and shortcuts that put them into practice, from the basics to recent additions, checked against the official docs.