In Kotlin, There Are No Primitives
Coming from Java, the first surprising thing about Kotlin’s type system is what’s missing: there are no primitive types. No lowercase int, no double, no boolean. In Kotlin, 42 is an Int, and Int is a class with methods you can call.
That sounds expensive — surely an object per number is slower than a raw int? It isn’t, because the compiler maps these types to JVM primitives wherever it can. You write a uniform, object-like model; you get primitive performance underneath. This post walks through the types you’ll use on day one.
val and var
Two ways to declare something. val is read-only, var is mutable:
val name = "Ada" // can't be reassigned
var age = 36 // can be reassigned
age = 37
You rarely write the type, because Kotlin infers it. Add it when you want to be explicit or when there’s no initializer:
val count: Int = 5
The habit to build: reach for val by default, and switch to var only when you genuinely need to reassign.
The number types
The familiar set, all as proper types: Int, Long, Short, Byte for whole numbers, Double and Float for decimals. Literals pick a type by their shape and suffix:
val i = 42 // Int
val l = 42L // Long
val d = 3.14 // Double
val f = 3.14f // Float
Long literals can use underscores for readability:
val population = 8_100_000_000L
No implicit conversions
Here’s a sharp edge worth knowing early. Java silently widens an int to a long. Kotlin refuses:
val i: Int = 42
// val l: Long = i // does NOT compile
val l: Long = i.toLong() // you convert explicitly
Every numeric type has toInt(), toLong(), toDouble(), and friends. It’s more typing, but it removes a category of silent precision and overflow bugs. (Arithmetic still promotes as you’d expect — 1L + 1 is a Long.)
Boolean and Char
val ready: Boolean = true
val grade: Char = 'A'
Char is its own type, not a number. In Java you can use 'A' as the int 65 directly; in Kotlin you ask for it: grade.code returns 65. Stronger typing, fewer accidental arithmetic-on-characters surprises.
Strings and templates
Strings are immutable. The feature you’ll use constantly is the string template — $ drops a value straight into the text:
val name = "Ada"
println("Hello, $name") // Hello, Ada
println("Next year: ${age + 1}") // ${ } for a full expression
Triple-quoted raw strings span multiple lines and ignore backslash escapes — handy for paths, JSON, and regex:
val path = """C:\Users\ada"""
val json = """
{ "name": "Ada" }
"""
null is part of the type
This is the one that changes how you write code. In Kotlin, String and String? are different types. A plain String can never hold null:
var name: String = "Ada"
// name = null // does NOT compile
var maybe: String? = "Ada"
maybe = null // fine — the ? permits null
Once a value can be null, the compiler makes you handle it. The safe-call ?. returns null instead of throwing:
val length: Int? = maybe?.length
The Elvis operator ?: supplies a fallback when the left side is null:
val length: Int = maybe?.length ?: 0
And !! asserts a value isn’t null, throwing if you’re wrong — the escape hatch you should rarely reach for:
val length: Int = maybe!!.length
This single design choice removes most NullPointerExceptions from everyday Kotlin. After a null check, the compiler even narrows String? to String for you — that’s smart casting, a topic of its own.
Any, Unit, and Nothing
Three special types worth meeting early:
Anyis the root of the hierarchy — the supertype of every non-null type, Kotlin’s equivalent ofObject. (Any?is the type that also includes null.)Unitis the type of “no meaningful value,” returned by functions that would bevoidin Java. Unlikevoid, it’s a real type with a single value — which matters once you start passing functions around.Nothingis the bottom type: the type of an expression that never returns, such as one that always throws. A function returningNothingtells the compiler that control stops there.
fun fail(message: String): Nothing = throw IllegalStateException(message)
val name = maybe ?: fail("name required")
// the compiler now knows `name` is a non-null String
Final thoughts
Kotlin trades a little up-front explicitness — no hidden numeric conversions, no char-as-int, conversions you have to spell out — for far fewer surprises at runtime. The headline win is null: by making it part of the type, Kotlin turns a whole class of NullPointerException into a compile error you fix before it ships.
This is the first post in a series working through Kotlin from the ground up. Next: when, the single construct that replaces switch, long if/else chains, and instanceof all at once.