- Write a class with a primary constructor, properties and methods
- Use what a data class gives you:
toString,==andcopy - Explain what
objectand companion objects are for - Build inheritance with
open/overrideand implement an interface
You are writing a class register: every student has a name, a grade level and scores, and you need to add new scores and calculate averages. With 30 students, separate variables quickly turn into a mess. Object-oriented programming offers another way: bundle the data and the functions that work with it into one place — an object. Kotlin does this with very short syntax.
Class, constructor and properties
A class is a blueprint for objects, and an object is a concrete instance built from it. In Kotlin the primary constructor is written in parentheses right after the class name: parameters declared there with val or var immediately become the class's properties. Functions inside a class are called methods. An object is created without the word new, like a function call: Student("Aysel", 8).
class Student(val name: String, var grade: Int) {
val scores = mutableListOf<Int>()
fun addScore(score: Int) {
scores.add(score)
}
fun average() = scores.average()
fun introduce() = "I am $name, grade $grade"
}
fun main() {
val aysel = Student("Aysel", 8)
aysel.addScore(5)
aysel.addScore(4)
println(aysel.introduce())
println(aysel.scores)
println(aysel.average())
aysel.grade = 9
println(aysel.introduce())
}I am Aysel, grade 8 [5, 4] 4.5 I am Aysel, grade 9
grade is a var, so it can change; name is a val and cannot.Data classes
If you print an object of an ordinary class, you see cryptic text like Student@1b6d3586, and two objects with the same data are not equal with ==. Very often a class exists just to hold data — a user, a product, an order. In such cases, write data in front of the class, and Kotlin automatically generates a readable toString, an == that compares contents, copy, and destructuring.
data class User(val name: String, val age: Int)
fun main() {
val u1 = User("Aysel", 15)
val u2 = User("Aysel", 15)
println(u1)
println(u1 == u2)
val older = u1.copy(age = 16)
println(older)
val (name, age) = older
println("$name is $age")
}User(name=Aysel, age=15) true User(name=Aysel, age=16) Aysel is 16
object and companion objects
Sometimes a program needs exactly one instance of something: the app settings or a database connection. For this, write object instead of class — it both defines the class and creates its only object. A companion object inside a class holds functions and constants that are called on the class name itself, without creating an object, for example “factory” functions that build objects.
object Settings {
var language = "az"
fun describe() = "Language: $language"
}
class Temperature(val celsius: Double) {
companion object {
const val WATER_BOILS = 100.0
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}
fun main() {
println(Settings.describe())
Settings.language = "en"
println(Settings.describe())
val t = Temperature.fromFahrenheit(212.0)
println(t.celsius)
println(t.celsius == Temperature.WATER_BOILS)
}Language: az Language: en 100.0 true
Inheritance: open and override
Inheritance lets you build a new class on top of an existing one: the child class gets the parent's properties and methods. In Kotlin classes are closed by default — to inherit from one, the parent class must be marked open. A method that a child may change must also be open, and the child writes its new version with override. The parent is given after a colon: class Dog(name: String) : Animal(name).
open class Animal(val name: String) {
open fun sound() = "..."
fun introduce() = "$name says ${sound()}"
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof!"
}
class Cat(name: String) : Animal(name) {
override fun sound() = "Meow!"
}
fun main() {
val pets = listOf(Dog("Rex"), Cat("Mestan"), Animal("Nemo"))
for (pet in pets) {
println(pet.introduce())
}
}Rex says Woof! Mestan says Meow! Nemo says ...
introduce() calls each object's own version of sound() — this is called polymorphism.Interfaces
An interface is a contract that describes what classes must be able to do: every class that implements its properties and methods belongs to the interface's type. An interface can also contain a ready-made version of a method. A class can inherit from only one class, but it can implement any number of interfaces.
interface Shape {
val name: String
fun area(): Double
fun describe() = "$name: area ${area()}"
}
class Rectangle(val width: Double, val height: Double) : Shape {
override val name = "Rectangle"
override fun area() = width * height
}
class Circle(val radius: Double) : Shape {
override val name = "Circle"
override fun area() = 3.14 * radius * radius
}
fun main() {
val shapes: List<Shape> = listOf(Rectangle(3.0, 4.0), Circle(2.0))
shapes.forEach { println(it.describe()) }
println(shapes.maxBy { it.area() }.name)
}Rectangle: area 12.0 Circle: area 12.56 Circle
kotlin.math.PI.| Declaration | When to use it |
|---|---|
class | ordinary objects with data and behaviour |
data class | objects that mainly hold data |
object | something that has exactly one instance |
open class | a base for child classes |
interface | a shared ability of several classes (a contract) |
Key points
class Student(val name: String)— the primary constructor declares the property directly; objects are created withoutnew.- A
data classautomatically getstoString, content-based==,copyand destructuring. objectcreates a single instance, and acompanion objectholds members called on the class name.- Classes and methods are closed by default: inheritance needs
open, and replacing a method needsoverride. - A class inherits from one parent class but can implement several interfaces.
Check yourself
10 questions. Every correct answer earns XP.
data class Point(val x: Int, val y: Int), what does println(Point(1, 2)) print?