Kotlin Nested Class and Inner Class

Understanding Kotlin Nested Class

A Kotlin nested class is a class defined inside another class without using the inner keyword. By default, nested classes in Kotlin are static in nature, similar to static nested classes in Java. This means a nested class cannot access the members of its outer class directly and doesn’t hold a reference to the outer class instance.

Basic Syntax of Nested Class

class OuterClass {  
    private val outerProperty = "I'm from outer class"  
      
    class NestedClass {  
        fun displayMessage() {  
            println("This is a nested class")  
            // Cannot access outerProperty here  
        }  
    }  
}  

Key Properties of Nested Class

Kotlin nested class has several important characteristics that distinguish it from inner classes:

  1. No access to outer class members: A nested class cannot directly access properties or methods of the outer class
  2. Independent instantiation: You can create objects of nested classes without creating an instance of the outer class
  3. Static behavior: Nested classes in Kotlin behave like static nested classes in Java
  4. Memory efficient: Since they don’t hold a reference to the outer class, they consume less memory

Creating Objects of Nested Class

To create an instance of a Kotlin nested class, you use the outer class name followed by a dot and the nested class name:

fun main() {  
    // Creating nested class object directly  
    val nestedObject = OuterClass.NestedClass()  
    nestedObject.displayMessage()  
}  

Understanding Kotlin Inner Class

A Kotlin inner class is declared using the inner keyword and can access all members of its outer class, including private members. Inner classes in Kotlin maintain a reference to the outer class instance, making them closely coupled with their containing class.

Basic Syntax of Inner Class

class OuterClass {  
    private val outerProperty = "I'm from outer class"  
      
    inner class InnerClass {  
        fun displayMessage() {  
            println("This is an inner class")  
            println("Accessing: $outerProperty") // Can access outer class members  
        }  
    }  
}  

Key Properties of Inner Class

Kotlin inner class provides several powerful features:

  1. Access to outer class members: Can access all properties and methods of the outer class, including private ones
  2. Reference to outer instance: Holds an implicit reference to the outer class instance
  3. Requires outer instance: Cannot be instantiated without first creating an outer class instance
  4. Qualified this expressions: Can use this@OuterClass to refer to the outer class instance

Creating Objects of Inner Class

To create an instance of a Kotlin inner class, you must first create an instance of the outer class:

fun main() {  
    val outerObject = OuterClass()  
    val innerObject = outerObject.InnerClass()  
    innerObject.displayMessage()  
}  

Nested Class vs Inner Class: Key Differences

Understanding when to use nested class versus inner class in Kotlin is essential for writing efficient code:

FeatureNested ClassInner Class
KeywordNone (default)inner
Outer class accessNoYes
Memory overheadLowerHigher
Instance creationOuterClass.NestedClass()outerInstance.InnerClass()
Reference to outerNoYes

Working with Qualified This Expressions

When working with Kotlin inner class, you might encounter situations where both the inner and outer classes have members with the same name. Qualified this expressions help resolve this ambiguity:

class Vehicle {  
    private val type = "Vehicle"  
      
    inner class Car {  
        private val type = "Car"  
          
        fun showTypes() {  
            println("Inner class type: $type")  
            println("Inner class type (explicit): ${this.type}")  
            println("Outer class type: ${this@Vehicle.type}")  
        }  
    }  
}  

Practical Examples

Example 1: File Manager System

Here’s a practical example showing how to use nested classes in Kotlin for organizing related functionality:

class FileManager {  
    private val files = mutableListOf<String>()  
      
    // Nested class for file operations that don't need access to instance data  
    class FileValidator {  
        fun isValidFileName(name: String): Boolean {  
            return name.isNotEmpty() && !name.contains("/") && !name.contains("\\")  
        }  
          
        fun getFileExtension(fileName: String): String {  
            return fileName.substringAfterLast(".", "")  
        }  
    }  
      
    // Inner class for operations that need access to the file list  
    inner class FileProcessor {  
        fun addFile(fileName: String) {  
            if (FileValidator().isValidFileName(fileName)) {  
                files.add(fileName) // Can access outer class property  
                println("File '$fileName' added successfully")  
            } else {  
                println("Invalid file name: $fileName")  
            }  
        }  
          
        fun listFiles() {  
            println("Files in manager: ${files.joinToString(", ")}")  
        }  
          
        fun getFileCount(): Int {  
            return files.size // Accessing outer class property  
        }  
    }  
}  

Example 2: Android RecyclerView Adapter Pattern

This example demonstrates a common Android development pattern using Kotlin inner class:

class ProductAdapter(private val products: List<Product>) : RecyclerView.Adapter<ProductAdapter.ViewHolder>() {  
      
    // Nested class - doesn't need access to adapter's products list for basic ViewHolder functionality  
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {  
        val titleView: TextView = itemView.findViewById(R.id.title)  
        val priceView: TextView = itemView.findViewById(R.id.price)  
          
        fun bindBasicLayout() {  
            // Basic layout setup that doesn't require adapter data  
            titleView.setTextColor(Color.BLACK)  
            priceView.setTextColor(Color.GREEN)  
        }  
    }  
      
    // Inner class for click handling that needs access to the products list  
    inner class ClickHandler {  
        fun onItemClick(position: Int) {  
            if (position < products.size) { // Can access outer class property  
                val product = products[position]  
                println("Clicked on product: ${product.name}")  
                // Handle navigation or other actions  
            }  
        }  
          
        fun onItemLongClick(position: Int) {  
            // Access adapter's data for long click actions  
            products[position].let { product ->  
                println("Long clicked on: ${product.name}")  
            }  
        }  
    }  
      
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {  
        val view = LayoutInflater.from(parent.context)  
            .inflate(R.layout.item_product, parent, false)  
        return ViewHolder(view)  
    }  
      
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {  
        val product = products[position]  
        holder.titleView.text = product.name  
        holder.priceView.text = "$${product.price}"  
          
        val clickHandler = ClickHandler()  
        holder.itemView.setOnClickListener { clickHandler.onItemClick(position) }  
        holder.itemView.setOnLongClickListener {   
            clickHandler.onItemLongClick(position)  
            true   
        }  
    }  
      
    override fun getItemCount(): Int = products.size  
}  
  
// Data class for the example  
data class Product(val name: String, val price: Double)  

Example 3: Database Connection Manager

This comprehensive example shows both nested class and inner class working together:

class DatabaseManager(private val connectionString: String) {  
    private var isConnected = false  
    private val queryHistory = mutableListOf<String>()  
      
    // Nested class for utility functions that don't need instance access  
    class QueryBuilder {  
        fun buildSelectQuery(table: String, columns: List<String>): String {  
            val columnStr = if (columns.isEmpty()) "*" else columns.joinToString(", ")  
            return "SELECT $columnStr FROM $table"  
        }  
          
        fun buildInsertQuery(table: String, data: Map<String, Any>): String {  
            val columns = data.keys.joinToString(", ")  
            val values = data.values.joinToString(", ") { "'$it'" }  
            return "INSERT INTO $table ($columns) VALUES ($values)"  
        }  
          
        fun buildUpdateQuery(table: String, data: Map<String, Any>, condition: String): String {  
            val setClause = data.map { "${it.key} = '${it.value}'" }.joinToString(", ")  
            return "UPDATE $table SET $setClause WHERE $condition"  
        }  
    }  
      
    // Inner class that needs access to connection and query history  
    inner class QueryExecutor {  
        fun executeQuery(query: String): QueryResult {  
            if (!isConnected) { // Accessing outer class property  
                throw IllegalStateException("Database not connected. Connection: $connectionString")  
            }  
              
            queryHistory.add(query) // Modifying outer class property  
            println("Executing query: $query")  
              
            // Simulate query execution  
            return QueryResult(true, "Query executed successfully")  
        }  
          
        fun executeWithHistory(query: String): QueryResult {  
            val result = executeQuery(query)  
            println("Query history count: ${queryHistory.size}")  
            return result  
        }  
          
        fun getLastQueries(count: Int): List<String> {  
            return queryHistory.takeLast(count) // Accessing outer class property  
        }  
          
        fun clearHistory() {  
            queryHistory.clear() // Modifying outer class state  
            println("Query history cleared")  
        }  
    }  
      
    fun connect(): Boolean {  
        isConnected = true  
        println("Connected to database: $connectionString")  
        return true  
    }  
      
    fun disconnect() {  
        isConnected = false  
        println("Disconnected from database")  
    }  
      
    fun getExecutor(): QueryExecutor {  
        return QueryExecutor()  
    }  
}  
  
// Result class for the example  
data class QueryResult(val success: Boolean, val message: String)  

Complete Working Example

Here’s a comprehensive example that demonstrates all concepts with proper imports and full implementation:

import java.util.*  
  
// Main application class demonstrating nested and inner classes  
class InventorySystem(private val systemName: String) {  
    private val items = mutableMapOf<String, Int>()  
    private val transactions = mutableListOf<String>()  
    private var systemStatus = "ONLINE"  
      
    // Nested class for utility functions that don't need system state access  
    class ItemValidator {  
        fun isValidItemCode(code: String): Boolean {  
            return code.length >= 3 && code.all { it.isLetterOrDigit() }  
        }  
          
        fun formatItemCode(code: String): String {  
            return code.uppercase().trim()  
        }  
          
        fun generateItemCode(): String {  
            return "ITEM_${Random().nextInt(1000, 9999)}"  
        }  
    }  
      
    // Another nested class for constants and configurations  
    class SystemConfig {  
        companion object {  
            const val MAX_ITEMS = 1000  
            const val MIN_QUANTITY = 0  
            const val MAX_QUANTITY = 999  
            const val SYSTEM_VERSION = "1.0.0"  
        }  
          
        fun getDefaultSettings(): Map<String, Any> {  
            return mapOf(  
                "maxItems" to MAX_ITEMS,  
                "minQuantity" to MIN_QUANTITY,  
                "maxQuantity" to MAX_QUANTITY,  
                "version" to SYSTEM_VERSION  
            )  
        }  
    }  
      
    // Inner class that needs access to system state and data  
    inner class InventoryManager {  
        fun addItem(itemCode: String, quantity: Int): Boolean {  
            val validator = ItemValidator()  
            val formattedCode = validator.formatItemCode(itemCode)  
              
            if (!validator.isValidItemCode(formattedCode)) {  
                println("Invalid item code: $itemCode")  
                return false  
            }  
              
            if (items.size >= SystemConfig.MAX_ITEMS) {  
                println("Cannot add item: Maximum inventory limit reached")  
                return false  
            }  
              
            if (quantity < SystemConfig.MIN_QUANTITY || quantity > SystemConfig.MAX_QUANTITY) {  
                println("Invalid quantity: $quantity")  
                return false  
            }  
              
            // Accessing and modifying outer class properties  
            items[formattedCode] = items.getOrDefault(formattedCode, 0) + quantity  
            val transaction = "ADDED: $formattedCode, Quantity: $quantity, Time: ${Date()}"  
            transactions.add(transaction)  
              
            println("Item added: $formattedCode with quantity $quantity")  
            println("System: $systemName - Status: $systemStatus")  
            return true  
        }  
          
        fun removeItem(itemCode: String, quantity: Int): Boolean {  
            val formattedCode = ItemValidator().formatItemCode(itemCode)  
            val currentQuantity = items[formattedCode] ?: 0  
              
            if (currentQuantity < quantity) {  
                println("Insufficient quantity. Available: $currentQuantity, Requested: $quantity")  
                return false  
            }  
              
            // Modifying outer class state  
            val newQuantity = currentQuantity - quantity  
            if (newQuantity == 0) {  
                items.remove(formattedCode)  
            } else {  
                items[formattedCode] = newQuantity  
            }  
              
            val transaction = "REMOVED: $formattedCode, Quantity: $quantity, Time: ${Date()}"  
            transactions.add(transaction)  
              
            println("Item removed: $formattedCode, quantity: $quantity")  
            return true  
        }  
          
        fun getInventoryStatus(): String {  
            return """  
                |System: $systemName  
                |Status: $systemStatus  
                |Total Items: ${items.size}  
                |Total Transactions: ${transactions.size}  
                |Items: ${items.entries.joinToString(", ") { "${it.key}=${it.value}" }}  
            """.trimMargin()  
        }  
          
        fun getRecentTransactions(count: Int): List<String> {  
            return transactions.takeLast(count)  
        }  
    }  
      
    // Another inner class for reporting that needs access to all system data  
    inner class ReportGenerator {  
        fun generateInventoryReport(): String {  
            val totalItems = items.values.sum()  
            val uniqueItems = items.size  
            val recentTransactions = transactions.takeLast(5)  
              
            return """  
                |=== INVENTORY REPORT ===  
                |System: $systemName  
                |Status: $systemStatus  
                |Report Generated: ${Date()}  
                |  
                |SUMMARY:  
                |- Unique Items: $uniqueItems  
                |- Total Quantity: $totalItems  
                |- Total Transactions: ${transactions.size}  
                |  
                |INVENTORY DETAILS:  
                |${items.entries.joinToString("\n") { "- ${it.key}: ${it.value}" }}  
                |  
                |RECENT TRANSACTIONS:  
                |${recentTransactions.joinToString("\n")}  
                |  
                |=== END REPORT ===  
            """.trimMargin()  
        }  
          
        fun generateLowStockAlert(threshold: Int = 5): String {  
            val lowStockItems = items.filter { it.value <= threshold }  
              
            return if (lowStockItems.isEmpty()) {  
                "No low stock items found (threshold: $threshold)"  
            } else {  
                """  
                |=== LOW STOCK ALERT ===  
                |System: $systemName  
                |Threshold: $threshold  
                |Low Stock Items: ${lowStockItems.size}  
                |  
                |${lowStockItems.entries.joinToString("\n") { "- ${it.key}: ${it.value}" }}  
                """.trimMargin()  
            }  
        }  
    }  
      
    fun startSystem() {  
        systemStatus = "ONLINE"  
        println("Inventory System '$systemName' started successfully")  
    }  
      
    fun stopSystem() {  
        systemStatus = "OFFLINE"  
        println("Inventory System '$systemName' stopped")  
    }  
      
    fun getManager(): InventoryManager = InventoryManager()  
    fun getReporter(): ReportGenerator = ReportGenerator()  
}  
  
// Main function demonstrating the complete example  
fun main() {  
    println("=== Kotlin Nested and Inner Class Demo ===\n")  
      
    // Create the main system  
    val inventorySystem = InventorySystem("Warehouse Management System")  
    inventorySystem.startSystem()  
      
    // Using nested class - can be accessed without instance  
    val validator = InventorySystem.ItemValidator()  
    val config = InventorySystem.SystemConfig()  
      
    println("Generated item code: ${validator.generateItemCode()}")  
    println("System configuration: ${config.getDefaultSettings()}")  
    println()  
      
    // Using inner classes - require instance of outer class  
    val manager = inventorySystem.getManager()  
    val reporter = inventorySystem.getReporter()  
      
    // Perform some operations  
    manager.addItem("ABC123", 50)  
    manager.addItem("XYZ789", 25)  
    manager.addItem("DEF456", 100)  
      
    manager.removeItem("ABC123", 10)  
      
    println("\n" + manager.getInventoryStatus())  
    println("\n" + reporter.generateInventoryReport())  
    println("\n" + reporter.generateLowStockAlert(30))  
      
    // Show recent transactions  
    println("\nRecent Transactions:")  
    manager.getRecentTransactions(3).forEach { println("- $it") }  
      
    inventorySystem.stopSystem()  
}  

Expected Output:

=== Kotlin Nested and Inner Class Demo ===  
  
Inventory System 'Warehouse Management System' started successfully  
Generated item code: ITEM_7642  
System configuration: {maxItems=1000, minQuantity=0, maxQuantity=999, version=1.0.0}  
  
Item added: ABC123 with quantity 50  
System: Warehouse Management System - Status: ONLINE  
Item added: XYZ789 with quantity 25  
System: Warehouse Management System - Status: ONLINE  
Item added: DEF456 with quantity 100  
System: Warehouse Management System - Status: ONLINE  
Item removed: ABC123, quantity: 10  
  
System: Warehouse Management System  
Status: ONLINE  
Total Items: 3  
Total Transactions: 4  
Items: ABC123=40, XYZ789=25, DEF456=100  
  
=== INVENTORY REPORT ===  
System: Warehouse Management System  
Status: ONLINE  
Report Generated: Wed Jun 18 10:30:45 PST 2025  
  
SUMMARY:  
- Unique Items: 3  
- Total Quantity: 165  
- Total Transactions: 4  
  
INVENTORY DETAILS:  
- ABC123: 40  
- XYZ789: 25  
- DEF456: 100  
  
RECENT TRANSACTIONS:  
ADDED: ABC123, Quantity: 50, Time: Wed Jun 18 10:30:45 PST 2025  
ADDED: XYZ789, Quantity: 25, Time: Wed Jun 18 10:30:45 PST 2025  
ADDED: DEF456, Quantity: 100, Time: Wed Jun 18 10:30:45 PST 2025  
REMOVED: ABC123, Quantity: 10, Time: Wed Jun 18 10:30:45 PST 2025  
  
=== END REPORT ===  
  
=== LOW STOCK ALERT ===  
System: Warehouse Management System  
Threshold: 30  
Low Stock Items: 1  
  
- XYZ789: 25  
  
Recent Transactions:  
- ADDED: XYZ789, Quantity: 25, Time: Wed Jun 18 10:30:45 PST 2025  
- ADDED: DEF456, Quantity: 100, Time: Wed Jun 18 10:30:45 PST 2025  
- REMOVED: ABC123, Quantity: 10, Time: Wed Jun 18 10:30:45 PST 2025  
  
Inventory System 'Warehouse Management System' stopped  

This comprehensive example demonstrates how Kotlin nested class and inner class can work together effectively. The nested classes (ItemValidator and SystemConfig) provide utility functions that don’t need access to the instance state, while the inner classes (InventoryManager and ReportGenerator) can access and modify the outer class properties to perform their operations.