Kotlin classes are concise and powerful. Data classes, sealed classes, and extension functions reduce boilerplate dramatically.
Basic Classes
Kotlin
class User(val name: String, var age: Int) {
// Primary constructor parameters with val/var become properties
init {
require(age >= 0) { "Age cannot be negative" }
}
// Secondary constructor
constructor(name: String) : this(name, 0)
// Property
val isAdult: Boolean
get() = age >= 18
// Method
fun greet() = "Hi, I'm $name!"
}
val user = User("Alice", 28)
println(user.name) // Alice
println(user.isAdult) // true
user.age = 29 // Mutable
// user.name = "Bob" // COMPILE ERROR (val)Data Classes
Kotlin
// Auto-generates: equals, hashCode, toString, copy, componentN
data class Point(val x: Double, val y: Double)
data class User(val name: String, val email: String, val age: Int)
val p1 = Point(1.0, 2.0)
val p2 = Point(1.0, 2.0)
p1 == p2 // true (structural equality)
p1.toString() // "Point(x=1.0, y=2.0)"
// Copy with modifications
val alice = User("Alice", "alice@test.com", 28)
val olderAlice = alice.copy(age = 29)
// Destructuring
val (name, email, age) = alice
println("$name is $age years old")
// Data class with defaults
data class Config(
val host: String = "localhost",
val port: Int = 8080,
val debug: Boolean = false
)
Config(debug = true) // host=localhost, port=8080, debug=trueSealed Classes
Kotlin
// Restricted class hierarchy — all subclasses in same file
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
data object Loading : Result<Nothing>()
}
// Exhaustive when expression (no else needed)
fun <T> handleResult(result: Result<T>): String = when (result) {
is Result.Success -> "Got data: ${result.data}"
is Result.Error -> "Error: ${result.exception.message}"
is Result.Loading -> "Please wait..."
}
// sealed interface (Kotlin 1.5+)
sealed interface Shape {
data class Circle(val radius: Double) : Shape
data class Rectangle(val width: Double, val height: Double) : Shape
}
fun area(shape: Shape): Double = when (shape) {
is Shape.Circle -> Math.PI * shape.radius * shape.radius
is Shape.Rectangle -> shape.width * shape.height
}Enum Classes
Kotlin
enum class Color(val hex: String) {
RED("#ff0000"),
GREEN("#00ff00"),
BLUE("#0000ff");
fun rgb() = hex.removePrefix("#").toInt(16)
}
val red = Color.RED
println(red.hex) // #ff0000
println(red.rgb()) // 16711680
// When with enum
fun describe(color: Color) = when (color) {
Color.RED -> "Warm"
Color.GREEN -> "Natural"
Color.BLUE -> "Cool"
}Interfaces
Kotlin
interface Drawable {
fun draw()
// Default implementation
fun highlight() = println("Highlighted!")
}
interface Resizable {
val minWidth: Int
fun resize(factor: Double)
}
class Button(val label: String) : Drawable, Resizable {
override val minWidth = 100
override fun draw() = println("Drawing button: $label")
override fun resize(factor: Double) = println("Resizing by $factor")
}Extension Functions
Kotlin
// Add methods to existing classes without modifying them
fun String.isPalindrome(): Boolean {
val cleaned = lowercase().filter { it.isLetterOrDigit() }
return cleaned == cleaned.reversed()
}
"racecar".isPalindrome() // true
"hello".isPalindrome() // false
// Extension properties
val String.wordCount: Int
get() = split("\\s+".toRegex()).size
"Hello World".wordCount // 2
// Nullable extensions
fun String?.orDefault(default: String = "N/A"): String {
return this ?: default
}
val name: String? = null
name.orDefault() // "N/A"💡 Tip: Use data classes for anything that represents data. Use sealed classes for representing states or variants. Use extension functions to add utility methods cleanly.