Classes & Constructors
Reviewed & published by Brayan K
Kotlin is a modern, concise language that makes classes remarkably compact — a primary constructor can declare and initialise properties in a single line, no boilerplate required.
Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll define classes with primary and secondary constructors, add methods, and run initialisation logic in init blocks.
What You'll Learn in This Lesson
- Declaring a class and creating instances (no new)
- Primary constructors that declare properties
- Adding member functions (methods)
- Running setup logic in init blocks
- Validating inputs with require
- Secondary constructors and : this(...)
1️⃣ Classes and Primary Constructors
A Kotlin class declares its primary constructor right after the class name. If you prefix a parameter with val or var, it becomes a property automatically. You create an instance by calling the class — there is no new keyword.
// The PRIMARY constructor goes right after the class name.
// Declaring parameters as val/var makes them properties automatically.
class Person(val name: String, var age: Int) {
// A member function (method) can use the properties.
fun birthday() {
age += 1
}
fun describe(): String = "$name is $age years old"
}
fun main() {
// Create an instance — NO 'new' keyword in Kotlin.
val ada = Person("Ada", 36)
println(ada.describe()) // -> Ada is 36 years old
ada.birthday() // mutates age (it's a var)
println(ada.describe()) // -> Ada is 37 years old
println(ada.name) // read a property -> Ada
}In Java this class would take a dozen lines of fields, a constructor, and getters. Kotlin condenses it to one expressive line plus the methods you actually care about.
2️⃣ init Blocks and Secondary Constructors
Setup logic that can't fit in the primary constructor goes in an init block, which runs during construction. require validates inputs and throws if they're invalid. A secondary constructor offers an alternative way to build the object and must delegate to the primary one with : this(...).
class Rectangle(val width: Int, val height: Int) {
val area: Int // declared, set in init
// The init block runs as part of construction.
init {
require(width > 0 && height > 0) { "Sides must be positive" }
area = width * height
println("Created ${width}x$height rectangle")
}
// A SECONDARY constructor must call the primary one with : this(...)
constructor(side: Int) : this(side, side) {
println("(square shortcut)")
}
}
fun main() {
val r = Rectangle(4, 3)
println("Area: ${r.area}") // -> Area: 12
val sq = Rectangle(5) // uses the secondary constructor
println("Square area: ${sq.area}") // -> Square area: 25
}Notice the order: the primary constructor's parameters are set, then the init block runs, then any secondary-constructor body. The require guard makes invalid objects impossible to create.
Your turn. Replace the TODO, then run and compare.
// 🎯 YOUR TURN — replace each TODO then run main()
// Define a class with a primary constructor that creates properties.
class BankAccount(val owner: String, var balance: Int) {
// 1) Add money — increase balance by 'amount'
fun deposit(amount: Int) {
balance TODO amount // 👉 replace TODO with '+='
}
fun describe() = "$owner: $balance"
}
fun main() {
val acc = BankAccount("Sam", 100)
acc.deposit(50)
println(acc.describe()) // expect: Sam: 150
}Mini-Challenge: A Counter Class
Define a small class with mutable state and two methods, then drive it from main().
// 🎯 MINI-CHALLENGE: a Counter class.
// Save as Counter.kt and run main().
//
// 1. Define class Counter(var count: Int = 0).
// 2. Add a method increment() that adds 1 to count.
// 3. Add a method reset() that sets count back to 0.
// 4. In main(), create a Counter, increment it three times,
// print the count, reset, then print again.
//
// ✅ Example output:
// Count: 3
// Count: 0
fun main() {
// your code here
}Common Errors (and the fix)
- Using new — Kotlin has no new. Just call the class: Person("Ada", 36).
- Forgetting val/var on a constructor param — Without it, the parameter isn't a property and obj.name won't compile. Add val or var.
- Reassigning a val property — You can't reassign a val property after construction. Use var if it changes.
- Secondary constructor not delegating — Every secondary constructor must call the primary with : this(...), or another secondary that eventually does.
Pro Tips
- 💡 Default params over secondary constructors: often a primary constructor with defaults removes the need for extra constructors.
- 💡 Validate early: use require in init to reject bad input at construction time.
- 💡 Keep methods small: a class is easiest to use when each method does one clear thing.
📋 Quick Reference — Classes
| Concept | Kotlin Syntax |
|---|---|
| Class + properties | class P(val n: String, var a: Int) |
| Create instance | val p = P("Ada", 36) |
| Method | fun describe() = "..." |
| init block | init { ... } |
| Validate | require(x > 0) { "..." } |
| Secondary ctor | constructor(s: Int) : this(s, s) |
🎉 Lesson Complete!
- ✅ Classes declare a primary constructor after the name
- ✅ val/var params become properties
- ✅ Create instances with no new keyword
- ✅ init blocks run setup; require validates
- ✅ Secondary constructors delegate with : this(...)
Practice quiz
Where does a Kotlin class declare its primary constructor?
- Inside an init block
- At the end of the class body
- Right after the class name
- In a companion object
Answer: Right after the class name. The primary constructor goes after the class name: class Person(...).
What does prefixing a primary constructor parameter with val or var do?
- Promotes it to a property of the class
- Makes it private
- Makes it static
- Marks it as nullable
Answer: Promotes it to a property of the class. val/var on a constructor parameter makes it a property automatically.
How do you create an instance of a class in Kotlin?
- Use the new keyword: new Person()
- Call Person.create()
- Use alloc Person
- Call the class like a function: Person("Ada", 36)
Answer: Call the class like a function: Person("Ada", 36). Kotlin has no new keyword; you call the class directly.
What is the init block used for?
- Declaring the primary constructor
- Initialization logic that runs during construction
- Defining methods
- Importing packages
Answer: Initialization logic that runs during construction. The primary constructor can't hold code, so setup logic goes in init.
What does require(width > 0) { "..." } do?
- Throws if the condition is false, validating input
- Logs a warning
- Returns false silently
- Defines a default value
Answer: Throws if the condition is false, validating input. require validates inputs and throws when the condition fails.
What must every secondary constructor do?
- Return a value
- Be private
- Delegate to the primary constructor with : this(...)
- Have no parameters
Answer: Delegate to the primary constructor with : this(...). Secondary constructors must ultimately call the primary one.
Can you reassign a val property after construction?
- Yes, always
- No, val properties are read-only once set
- Only inside init
- Only with var keyword
Answer: No, val properties are read-only once set. Use var if the property needs to change after construction.
In class Person(val name: String, var age: Int), which property can birthday() mutate with age += 1?
- name, because it is a val
- Both
- Neither
- age, because it is a var
Answer: age, because it is a var. Only var properties can be reassigned; name is a val.
If a constructor parameter has no val or var, can you access it as obj.property?
- Yes, always
- No, it is only available inside the constructor and init blocks
- Only if it is an Int
- Only from a companion object
Answer: No, it is only available inside the constructor and init blocks. Without val/var the parameter is not a property.
What often removes the need for a secondary constructor?
- Using new
- Adding more init blocks
- A primary constructor with default parameter values
- Making the class abstract
Answer: A primary constructor with default parameter values. Default parameters frequently replace extra constructors idiomatically.
Continue this course
- Previous: Collection Operations (map, filter, fold)
- Next: Properties — Custom getters and setters, lateinit, and lazy properties
- Quick reference: Kotlin cheat sheet › Classes & Objects
Frequently asked questions
Why is there no 'new' keyword in Kotlin?
Kotlin deliberately omits new to reduce noise. You create an instance simply by calling the class like a function: val p = Person("Ada", 36). This makes object creation look the same as a function call and keeps code clean. Under the hood the constructor still runs exactly as you'd expect.
What does declaring a constructor parameter as val or var do?
Putting val or var in front of a primary constructor parameter promotes it to a property of the class. So class Person(val name: String) both receives name as a constructor argument and stores it as a read-only property accessible via person.name. Without val or var, the parameter is only available inside the constructor and init blocks, not as a property.
What is the init block for?
The primary constructor itself cannot contain code, so any initialization logic — validation, computing derived properties, logging — goes in an init block. init blocks run in the order they appear, as part of constructing the object, after the primary constructor parameters are available. You can have several init blocks, and they often pair with the require function to validate inputs.
When do I need a secondary constructor?
Most of the time the primary constructor plus default parameter values is enough, and that is the idiomatic Kotlin approach. Secondary constructors, declared with the constructor keyword, are useful when you need an alternative way to build the object — for example a Rectangle(side) shortcut that delegates to the primary constructor with : this(side, side). Every secondary constructor must ultimately call the primary one.