In object-oriented programming with Kotlin, a class groups related data (properties) and behavior (functions/methods) into one reusable unit.
Classes act as blueprints from which we create objects (also called instances). Each object has its own copy of instance data
(properties defined in the class body), while class-level data (e.g., in companion object or top-level properties) can be shared across instances.
Behavior is shared by default and can be customized or extended in many ways — most commonly through inheritance and overriding, but
also through composition, delegation (using by), interface default implementations, extension
functions, and properties.
Besides using classes very similar to Java, Kotlin introduces several modern class kinds that provide new capabilities not easily achievable in classic OOP, while also reducing boilerplate in many common cases. These new kinds do not merely reduce code — they add expressive power, stronger type safety, performance benefits, and enable patterns that are difficult or impossible with traditional Java classes.
Kotlin Class Types
- Regular class — standard class with properties and methods
- Data class — automatically generates
equals(),hashCode(),toString(),copy(), andcomponentN() - Enum class — fixed set of constants
- Sealed class — restricted hierarchy, perfect for
whenexpressions - Abstract class — partial implementation, cannot be instantiated directly
- Interface — contract for behavior, supports default implementations
- Object declaration — singleton (only one instance exists)
- Nested / Inner class — class inside another class (inner has access to outer instance)
- Inline class (value class) — wraps a single value with zero runtime overhead
1. Regular Class
class Person(val name: String, var age: Int) {
fun greet() = println("Hello, I'm $name and $age years old")
}
val alice = Person("Alice", 30)
alice.greet() // Hello, I'm Alice and 30 years old
2. Data Class
Automatically provides useful methods — no boilerplate needed.
data class User(val id: Int, val name: String)
val user1 = User(1, "Bob")
val user2 = user1.copy(name = "Bobby")
println(user1) // User(id=1, name=Bob)
println(user1 == User(1, "Bob")) // true (structural equality)
println(user2) // User(id=1, name=Bobby)
3. Enum Class
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
val dir = Direction.EAST
println(dir) // EAST
4. Sealed Class
Restricted hierarchy — all subclasses must be defined in the same file.
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
fun handle(result: Result) = when (result) {
is Success -> println("Success: ${result.data}")
is Error -> println("Error: ${result.message}")
Loading -> println("Loading...")
// No 'else' needed — compiler knows all cases
}
5. Abstract Class
abstract class Animal {
abstract fun sound()
fun sleep() = println("Zzz...")
}
class Dog : Animal() {
override fun sound() = println("Woof!")
}
val dog = Dog()
dog.sound() // Woof!
dog.sleep() // Zzz...
6. Interface
interface Printable {
fun print() // abstract
fun preview() = println("Previewing...") // default implementation
}
class Document : Printable {
override fun print() = println("Printing document")
}
val doc = Document()
doc.print() // Printing document
doc.preview() // Previewing...
7. Object Declaration (Singleton)
object Logger {
fun info(msg: String) = println("INFO: $msg")
fun error(msg: String) = println("ERROR: $msg")
}
// Used directly — no instance needed
Logger.info("App started")
8. Nested vs Inner Class
class Outer {
private val secret = "I'm hidden"
class Nested { // static-like
fun hello() = "Hello from nested"
}
inner class Inner { // holds reference to Outer
fun reveal() = secret
}
}
println(Outer.Nested().hello()) // Hello from nested
val outer = Outer()
println(outer.Inner().reveal()) // I'm hidden
9. Inline (Value) Class
Wraps a single value with zero runtime overhead — perfect for type-safe IDs, units, etc.
@JvmInline
value class UserId(val value: Int)
fun process(id: UserId) = println("Processing user ${id.value}")
val uid = UserId(123)
process(uid) // Processing user 123
// At runtime: just an Int — no extra object created