Kotlin Nested & Inner Classes

Organizing classes within classes for better structure

🏠 What are Nested & Inner Classes?

Nested classes are classes defined inside other classes for better organization. Inner classes can access outer class members, while nested classes are static-like. Both help group related functionality and improve code structure and encapsulation.


// Nested class example
class Outer {
    class Nested {
        fun greet() = "Hello from nested!"
    }
}
                                    

Key Concepts

📦

Nested Classes

Static-like classes inside other classes

class Outer {
    class Nested {
        fun hello() = "Hi!"
    }
}
🔗

Inner Classes

Can access outer class members

class Outer {
    private val name = "Outer"
    inner class Inner {
        fun getName() = name
    }
}
🏷️

Anonymous Classes

Classes without explicit names

val obj = object : Interface {
    override fun method() = "Anonymous"
}
🎯

Local Classes

Classes defined inside functions

fun createLocal() {
    class Local {
        fun greet() = "Local class"
    }
}

🔹 Nested Classes

Nested classes don't have access to outer class instance:

class Computer {
    private val brand = "Dell"
    
    class Processor {
        val cores = 4
        val speed = "3.2 GHz"
        
        fun getInfo(): String {
            // Cannot access 'brand' here
            return "Processor: $cores cores at $speed"
        }
    }
    
    fun getComputerInfo(): String {
        val processor = Processor()
        return "Brand: $brand, ${processor.getInfo()}"
    }
}

fun main() {
    // Create nested class without outer instance
    val processor = Computer.Processor()
    println(processor.getInfo())
    
    // Create outer class and use nested class
    val computer = Computer()
    println(computer.getComputerInfo())
}

Output:

Processor: 4 cores at 3.2 GHz
Brand: Dell, Processor: 4 cores at 3.2 GHz

🔹 Inner Classes

Inner classes can access outer class members and need outer instance:

class House {
    private val address = "123 Main St"
    private val rooms = mutableListOf<String>()
    
    inner class Room(private val name: String) {
        fun addToHouse() {
            // Can access outer class members
            rooms.add(name)
            println("Added $name to house at $address")
        }
        
        fun getHouseAddress() = address
    }
    
    fun addRoom(roomName: String) {
        val room = Room(roomName)
        room.addToHouse()
    }
    
    fun listRooms() {
        println("Rooms in house: ${rooms.joinToString(", ")}")
    }
}

fun main() {
    val house = House()
    house.addRoom("Living Room")
    house.addRoom("Kitchen")
    house.listRooms()
    
    // Create inner class with outer instance
    val bedroom = house.Room("Bedroom")
    println("House address: ${bedroom.getHouseAddress()}")
}

Output:

Added Living Room to house at 123 Main St
Added Kitchen to house at 123 Main St
Rooms in house: Living Room, Kitchen
House address: 123 Main St

🔹 Anonymous Inner Classes

Create classes on-the-fly using object expressions:

interface ClickListener {
    fun onClick()
    fun onDoubleClick()
}

class Button {
    private var listener: ClickListener? = null
    
    fun setClickListener(listener: ClickListener) {
        this.listener = listener
    }
    
    fun click() {
        listener?.onClick()
    }
    
    fun doubleClick() {
        listener?.onDoubleClick()
    }
}

fun main() {
    val button = Button()
    
    // Anonymous inner class
    button.setClickListener(object : ClickListener {
        override fun onClick() {
            println("Button clicked!")
        }
        
        override fun onDoubleClick() {
            println("Button double-clicked!")
        }
    })
    
    button.click()
    button.doubleClick()
}

Output:

Button clicked!
Button double-clicked!

🔹 Local Classes

Classes defined inside functions can access local variables:

fun createCalculator(operation: String): Any {
    val operationName = operation.uppercase()
    
    // Local class
    class Calculator {
        fun add(a: Int, b: Int): String {
            // Can access local variables
            return "$operationName: ${a + b}"
        }
        
        fun multiply(a: Int, b: Int): String {
            return "$operationName: ${a * b}"
        }
    }
    
    return Calculator()
}

fun main() {
    val addCalculator = createCalculator("addition") as Calculator
    val multiplyCalculator = createCalculator("multiplication") as Calculator
    
    // Note: This is a simplified example
    // In practice, you'd use interfaces or sealed classes
    println("5 + 3 = ${addCalculator.add(5, 3)}")
    println("4 * 6 = ${multiplyCalculator.multiply(4, 6)}")
}

🔹 Practical Example: Data Structure

Using nested and inner classes to build a linked list:

class LinkedList<T> {
    private var head: Node<T>? = null
    private var size = 0
    
    // Nested class for Node
    class Node<T>(val data: T) {
        var next: Node<T>? = null
    }
    
    // Inner class for Iterator
    inner class Iterator {
        private var current = head
        
        fun hasNext(): Boolean = current != null
        
        fun next(): T? {
            val data = current?.data
            current = current?.next
            return data
        }
    }
    
    fun add(data: T) {
        val newNode = Node(data)
        newNode.next = head
        head = newNode
        size++
    }
    
    fun iterator(): Iterator = Iterator()
    
    fun getSize(): Int = size
}

fun main() {
    val list = LinkedList<String>()
    list.add("Third")
    list.add("Second")
    list.add("First")
    
    println("List size: ${list.getSize()}")
    
    val iterator = list.iterator()
    print("Elements: ")
    while (iterator.hasNext()) {
        print("${iterator.next()} ")
    }
}

Output:

List size: 3
Elements: First Second Third

🧠 Test Your Knowledge

What's the main difference between nested and inner classes?