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: ${this@Vehicle.type}")
}
}
}
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.