Skip to content
Classes and Constructors
step 1/7

Reading — step 1 of 7

Learn

~3 min readClasses and Inheritance

Kotlin classes are concise. The primary constructor is part of the class header; properties are declared inline. Kotlin in Action treats this as foundational — most Kotlin types are classes, and the syntax pays back from line one.

Primary constructor

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

That's a complete class. The constructor parameters are simultaneously property declarations:

  • val name: String — read-only property + ctor parameter
  • var age: Int — mutable property + ctor parameter

Use it:

val ada = Person("Ada", 36)
println(ada.name)         // Ada
ada.age = 37              // OK — var
// ada.name = "Linus"    // ERROR — val

No new keyword. No separate field declarations. No boilerplate getters/setters.

init blocks — initialization logic

class User(val name: String, age: Int) {
    val displayName: String
    
    init {
        require(age >= 0) { "age must be non-negative" }
        displayName = if (name.isBlank()) "Anonymous" else name
    }
}

init runs as part of the primary constructor. Multiple init blocks run in order.

Note: age here doesn't have val/var — it's just a constructor parameter, not a property.

Secondary constructors

class Rectangle(val width: Int, val height: Int) {
    constructor(side: Int) : this(side, side)         // delegates to primary
}

val sq = Rectangle(side = 5)         // 5x5
val r = Rectangle(width = 4, height = 6)

Secondary constructors must call the primary (directly or transitively) via this(...). Allows alternative initialization paths.

Default arguments and named parameters

class HttpClient(
    val baseUrl: String,
    val timeoutMs: Int = 5000,
    val retries: Int = 3
)

val client = HttpClient("https://api.example.com")     // uses defaults
val custom = HttpClient("https://api.example.com", retries = 5)   // named

Most Kotlin code avoids constructor overload chains — defaults + named args replace them.

Properties with custom getters/setters

class Temperature(celsius: Double) {
    var celsius: Double = celsius
        set(value) {
            require(value >= -273.15) { "below absolute zero" }
            field = value         // 'field' is the backing field
        }
    
    val fahrenheit: Double
        get() = celsius * 9 / 5 + 32
}

field is the magic backing-field reference inside accessors. fahrenheit has only a getter (computed on access).

visibility modifiers

  • public (default) — visible everywhere
  • internal — visible within the same module
  • protected — only this class and subclasses
  • private — only this class (or this file for top-level)
class BankAccount {
    private var balance: Double = 0.0
    fun deposit(amount: Double) { balance += amount }
    fun getBalance(): Double = balance
}

Final by default

Classes and methods are final (closed for inheritance) unless marked open. The opposite of Java. Encourages composition over inheritance.

open class Animal {
    open fun speak() = "generic sound"
}

class Dog : Animal() {       // notice the parens — calling Animal's primary ctor
    override fun speak() = "woof"
}

Common mistakes

  • Forgetting val/var on constructor parameters — without it, the parameter ISN'T a property, just a ctor param. Surprising when you can't access it later.
  • Calling parent constructor with wrong syntaxclass Dog : Animal() has parens; not class Dog : Animal.
  • Missing open and trying to subclass or override — Kotlin defaults to final. Need explicit open on the parent.
  • Trying to use private to hide from subclasses — use protected for that.
  • Using init to set vals declared in primary — already happened; redundant.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…