Kotlin Inner and Nested Classes
Kotlin lets you define a class inside another class. This is useful for logically grouping related types together. A nested class has no connection to its outer class. An inner class (marked with inner) holds a reference to its outer class and can access its members.
Nested Class (No Outer Reference)
class Laptop(val brand: String) {
class Battery(val capacityMah: Int) { // nested — no access to Laptop
fun info() = "Battery: ${capacityMah}mAh"
}
fun describe() = "$brand laptop"
}
fun main() {
// Create Battery without creating a Laptop first
val battery = Laptop.Battery(5000)
println(battery.info()) // Battery: 5000mAh
val laptop = Laptop("Dell")
println(laptop.describe())
}Inner Class (Has Outer Reference)
class Car(val model: String) {
inner class Engine(val horsepower: Int) { // inner — can access Car's members
fun describe() = "$model engine with ${horsepower}hp"
// ↑ accessing outer class's model property
}
}
fun main() {
val car = Car("Tesla Model 3")
val engine = car.Engine(450) // must create through a Car instance
println(engine.describe()) // Tesla Model 3 engine with 450hp
}Nested vs Inner: Key Difference
Nested class:
class Outer {
class Nested {
// Cannot access Outer's members
// Created as: Outer.Nested()
}
}
Inner class:
class Outer(val data: String) {
inner class Inner {
fun use() = data // CAN access Outer's members
// Created as: Outer("x").Inner()
}
}
Accessing Outer Class with this@Outer
class Page(val number: Int) {
inner class Header(val title: String) {
fun display() {
// Use this@ to distinguish outer from inner 'this'
println("Page ${this@Page.number}: $title")
}
}
inner class Footer(val text: String) {
fun display() = println("Page ${this@Page.number} — $text")
}
}
fun main() {
val page = Page(5)
page.Header("Chapter 2: Functions").display() // Page 5: Chapter 2: Functions
page.Footer("© 2024 Kotlin Course").display() // Page 5 — © 2024 Kotlin Course
}Nested Classes as Namespacing
// Group related types inside a parent class for clarity
class Network {
data class Request(val url: String, val method: String = "GET")
data class Response(val statusCode: Int, val body: String)
class Client {
fun send(request: Request): Response {
println("Sending ${request.method} to ${request.url}")
return Response(200, "OK")
}
}
}
fun main() {
val client = Network.Client()
val request = Network.Request("https://api.example.com/users")
val response = client.send(request)
println("${response.statusCode}: ${response.body}")
}Anonymous Inner Class
interface ClickListener {
fun onClick(buttonName: String)
}
class Button(val name: String) {
var listener: ClickListener? = null
fun click() {
listener?.onClick(name)
}
}
fun main() {
val button = Button("Submit")
// Anonymous inner class — one-off implementation
button.listener = object : ClickListener {
override fun onClick(buttonName: String) {
println("$buttonName was clicked!")
}
}
button.click() // Submit was clicked!
}Practical Example: Report with Sections
class Report(val title: String) {
inner class Section(val heading: String) {
private val lines = mutableListOf()
fun addLine(text: String) = lines.add(text)
fun render(): String = buildString {
appendLine(" ── $heading ──")
lines.forEach { appendLine(" $it") }
}
fun reportTitle() = this@Report.title // access outer class
}
fun newSection(heading: String) = Section(heading)
}
fun main() {
val report = Report("Q2 Sales Report")
val summary = report.newSection("Summary")
summary.addLine("Total revenue: ₹12,45,000")
summary.addLine("Units sold: 1,840")
val top = report.newSection("Top Products")
top.addLine("1. Laptop – 320 units")
top.addLine("2. Monitor – 290 units")
println(report.title)
print(summary.render())
print(top.render())
} Output:
Q2 Sales Report
── Summary ──
Total revenue: ₹12,45,000
Units sold: 1,840
── Top Products ──
1. Laptop – 320 units
2. Monitor – 290 units