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.