So far you've stored data in separate variables and lists. Real programs are about things: a bank account with a balance, a player with a name and a score, a product with a price and stock. A class describes one kind of thing, bundling its data (properties) with the actions it can perform (methods). An object is one particular thing made from that class: Sam's account, or the backpack product.
Kotlin's classes are remarkably concise. Properties can be declared right in the constructor, so a class that would take twenty lines in some languages fits on one. Objects are created without a new keyword, and properties replace the getter and setter methods you may know from Java.
In this lesson you'll write classes with constructors, properties and methods, validate data in init blocks, control access with private, and add custom getters and setters. Classes are the foundation for the rest of the intermediate stage.
A first class
class Player(val name: String, var score: Int = 0) {
fun addPoints(points: Int) {
score += points
}
}
val p = Player("Ana")
p.addPoints(10)
println("${p.name}: ${p.score}") // Ana: 10- The brackets after the class name are the primary constructor.
valandvarin the constructor create properties:namecan't change,scorecan.- Parameters can have default values, just like functions.
- Create an object by calling the class like a function. There's no
new. - Methods are functions inside the class and can use the properties directly.
init blocks and validation
Code in an init block runs when an object is created, which makes it the place to check that the data is valid:
class Temperature(val celsius: Double) {
init {
require(celsius >= -273.15) { "Below absolute zero: $celsius" }
}
}require(condition) { message } throws an IllegalArgumentException if the condition is false. Checking data at creation means every object of the class is guaranteed to be valid, so the rest of your code doesn't have to keep checking.
Properties: getters, setters and visibility
A property can calculate its value every time it's read with a custom getter:
class Rectangle(val width: Double, val height: Double) {
val area: Double get() = width * height
}A custom setter runs when a var is assigned; field is the stored value:
var name: String = ""
set(value) { field = value.trim() }Control who can see what with visibility modifiers: public (the default, visible everywhere), private (only inside the class), protected (the class and its subclasses) and internal (the same module). var balance = 0.0 private set lets anyone read the balance but only the class change it.
Secondary constructors and lateinit
Most classes need only the primary constructor, often with default values. If you need another way to create objects, add a secondary constructor with constructor(...), which must call the primary one with this(...).
lateinit var declares a non-null property that will be set later, for example after a screen is created in Android. Reading it before it's set throws an exception, so use it sparingly; a nullable type or a default value is usually safer.
Variables hold references to objects
When you assign an object to another variable, Kotlin doesn't copy it. Both names point at the same object, so a change through one is visible through the other:
val a = Player("Ana")
val b = a
b.score = 20
println(a.score) // 20
fun bonus(p: Player) { p.score += 5 }
bonus(a)
println(a.score) // 25The same happens when you pass an object to a function: bonus receives a reference to Ana's player, not a copy, so its change sticks. The val in val a only stops a from being pointed at a different player; it doesn't freeze the player's own var properties.
This also explains how ordinary classes compare. === asks "are these the very same object?", and for a plain class, == asks the same question unless you override equals. Two separate players with identical names and scores are not equal:
println(Player("Ana", 25) == Player("Ana", 25)) // false
println(a === b) // truePrinting a plain object shows its class name and a code such as Point@1b6d3586, which isn't much help. Override toString to choose what's printed:
class Player(val name: String, var score: Int = 0) {
override fun toString() = "Player($name, $score)"
}If you find yourself writing equals and toString by hand for a class that mainly holds data, a data class generates both for you.
Kotlin Classes Syntax
Class syntax
class Name(val a: Type, var b: Type = default) {
init { require(...) { "message" } }
val derived: Type get() = ...
var x: Int = 0
private set
fun method() { }
private fun helper() { }
}
val obj = Name(a, b)
obj.method()Kotlin Classes Summary
- A class bundles properties and methods; an object is one instance, created without new.
- val and var in the primary constructor declare properties directly.
- init blocks run at creation and are the place for require checks.
- Custom getters calculate values; custom setters use field; private set restricts writing.
- Visibility: public by default, private, protected and internal.
Kotlin Classes Examples
Each example shows the code, the output it produces and what each part does.
A bank account
class Account(val owner: String, initial: Double = 0.0) {
var balance = initial
private set
fun deposit(amount: Double) {
require(amount > 0) { "Deposit must be positive" }
balance += amount
}
fun withdraw(amount: Double): Boolean {
if (amount > balance) return false
balance -= amount
return true
}
}
fun main() {
val acc = Account("Sam", 100.0)
acc.deposit(50.0)
println(acc.withdraw(30.0))
println(acc.withdraw(500.0))
println("${acc.owner} has £${acc.balance}")
}true
false
Sam has £120.0Line-by-line explanation
- Line 1initial isn't a property, just a constructor parameter used to set balance.
- Lines 2 to 3Anyone can read balance, but only the class can change it.
- Line 6require rejects negative deposits.
- Line 21The withdrawal fails because the balance is too low.
Validation and computed properties
class Rectangle(val width: Double, val height: Double) {
init {
require(width > 0 && height > 0) { "Sides must be positive" }
}
val area: Double get() = width * height
val isSquare: Boolean get() = width == height
fun describe() = "${width}x$height, area $area"
}
fun main() {
val r = Rectangle(4.0, 2.5)
println(r.describe())
println(r.isSquare)
try {
Rectangle(-1.0, 3.0)
} catch (e: IllegalArgumentException) {
println("Error: ${e.message}")
}
}4.0x2.5, area 10.0
false
Error: Sides must be positiveLine-by-line explanation
- Lines 2 to 4init runs for every new Rectangle and rejects invalid sizes.
- Lines 6 to 7Computed properties have no stored value; they're worked out when read.
- Line 9${width} needs braces because an x follows straight after the name.
- Lines 17 to 21Creating an invalid object throws, which try and catch handle; the exceptions lesson covers this fully.
A custom setter
class Profile(name: String) {
var name: String = name.trim()
set(value) {
field = value.trim().replaceFirstChar { it.uppercase() }
}
var visits = 0
private set
fun visit() { visits++ }
}
fun main() {
val p = Profile(" ada ")
println("[${p.name}]")
p.name = " grace hopper"
println("[${p.name}]")
p.visit(); p.visit()
println(p.visits)
}[ada]
[Grace hopper]
2Line-by-line explanation
- Line 2The initial value is trimmed, but the setter doesn't run for the initial value, so it isn't capitalised.
- Lines 3 to 5Every later assignment runs the setter; field is the stored value.
- Line 18Two statements on one line need a semicolon between them.
Common Kotlin Classes Mistakes
Using new
val p = new Player("Ana") // error: new isn't a Kotlin keywordval p = Player("Ana")Forgetting val or var in the constructor
class User(name: String)
println(User("Ana").name) // error: Unresolved reference: nameclass User(val name: String)Without val or var, a constructor parameter isn't a property.
A setter that calls itself
var age: Int = 0
set(value) { age = maxOf(0, value) } // infinite recursionvar age: Int = 0
set(value) { field = maxOf(0, value) }Kotlin Classes in Real Projects
Classes in Kotlin apps
Android apps model their data with classes such as User, Message and Order, and their behaviour with classes like ViewModel and Repository. Server frameworks like Spring Boot and Ktor organise endpoints and services as classes too. Kotlin's compact constructors are a big part of why Kotlin files are typically much shorter than the equivalent Java.
Kotlin Classes FAQ
What is the difference between a class and an object in Kotlin?
A class is the blueprint: it describes which properties and functions something has. An object, or instance, is one concrete thing built from that blueprint, such as Player("Ana"). You can create as many objects from one class as you like, each with its own property values.
What is the difference between a property and a constructor parameter?
A constructor parameter written with val or var becomes a property that lives as long as the object. Without val or var, it's a plain parameter, available only in init blocks and property initialisers. Use plain parameters for values you only need while setting the object up.
When should I use lateinit instead of a nullable property?
Use lateinit var for a non-null value that's guaranteed to be set before it's read but can't be set in the constructor, such as something a framework injects. If the value can genuinely be missing, use a nullable type. You can check ::name.isInitialized inside the class, but needing that check often means a nullable type would be clearer.
Are Kotlin classes public by default?
Yes. Classes, functions and properties are public unless you say otherwise. Mark details as private so outside code can't depend on them, and use internal for things that should be visible across your module but not to code that uses it as a library.
How do I make a read-only property that the class can still change?
Declare it as a var with a private setter: var balance = 0.0 followed by private set on the next line. Outside code can read account.balance but can't assign to it, while functions inside the class, such as deposit, can update it.
Kotlin Classes Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the constructor so name becomes a read-only property.
class City( name: String)
What does this print?
class Counter(var n: Int = 0) {
fun up() { n++ }
}
val c = Counter(5)
c.up(); c.up()
println(c.n)
What does private set on a property do?
Where should you check that constructor values are valid?
What does this print?
class Box(val w: Int, val h: Int) {
val area get() = w * h
}
val b = Box(3, 4)
println(b.area + Box(2, 2).area)
Kotlin Classes Quiz
5 questions to check what you learned.
How do you create an object of class Player?
- Player()
- new Player()
- Player.create()
- create Player()
What is
fieldinside a custom setter?- The class name
- A new variable
- The property's stored value
- The parameter
What is the default visibility of a Kotlin class member?
- private
- public
- internal
- protected
What does
require(x > 0) { "msg" }do when x is -1?- Returns false
- Prints the message
- Sets x to 0
- Throws an IllegalArgumentException with the message
What's the risk of
lateinit var?- It makes the class slower
- Reading it before it's assigned throws an exception
- It can only hold Int values
- It's always null