
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.
class OuterClass {
private val outerProperty = "I'm from outer class"
class NestedClass {
fun displayMessage() {
println("This is a nested class")
// Cannot access outerProperty here
}
}
}
Kotlin nested class has several important characteristics that distinguish it from inner classes:
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()
}
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.
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
}
}
}
Kotlin inner class provides several powerful features:
this@OuterClass to refer to the outer class instanceTo 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()
}
Understanding when to use nested class versus inner class in Kotlin is essential for writing efficient code:
| Feature | Nested Class | Inner Class |
|---|---|---|
| Keyword | None (default) | inner |
| Outer class access | No | Yes |
| Memory overhead | Lower | Higher |
| Instance creation | OuterClass.NestedClass() | outerInstance.InnerClass() |
| Reference to outer | No | Yes |
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: ${[email protected]}")
}
}
}
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
}
}
}
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)
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)
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.