Skip to content

Program Structure

Overview

This chapter introduces the organizational structure of Kotlin programs, including packages, files, classes, objects, interfaces, and other core concepts. Understanding these structures helps you write well-organized, maintainable Kotlin code.

Package Structure

Package Declaration

kotlin
// File: src/main/kotlin/com/example/myapp/models/User.kt
package com.example.myapp.models

class User(val name: String, val email: String)

Package Organization Principles

kotlin
// Organize package structure by functionality
com.example.myapp/
├── models/          // Data models
│   ├── User.kt
│   ├── Product.kt
│   └── Order.kt
├── services/        // Business logic
│   ├── UserService.kt
│   └── OrderService.kt
├── repositories/    // Data access
│   ├── UserRepository.kt
│   └── OrderRepository.kt
├── controllers/     // Controllers (web apps)
│   └── UserController.kt
└── utils/          // Utility classes
    ├── DateUtils.kt
    └── StringUtils.kt

Top-Level Declarations

kotlin
// File: MathUtils.kt
package com.example.utils

// Top-level function
fun calculateArea(radius: Double): Double = Math.PI * radius * radius

// Top-level property
const val PI_APPROXIMATION = 3.14159

// Top-level class
class Calculator {
    fun add(a: Int, b: Int) = a + b
}

// Top-level object
object MathConstants {
    const val E = 2.71828
    const val GOLDEN_RATIO = 1.61803
}

Class Structure

Basic Class Definition

kotlin
// Simple class
class Person {
    // Properties
    var name: String = ""
    var age: Int = 0
    
    // Methods
    fun introduce() {
        println("Hi, I'm $name, $age years old")
    }
}

// Class with primary constructor
class Student(val name: String, val studentId: String) {
    // Secondary constructor
    constructor(name: String, studentId: String, grade: Int) : this(name, studentId) {
        this.grade = grade
    }
    
    // Property
    var grade: Int = 1
    
    // Initializer block
    init {
        println("Student $name created with ID $studentId")
    }
    
    // Method
    fun study(subject: String) {
        println("$name is studying $subject")
    }
}

Class Visibility Modifiers

kotlin
// Default is public
class PublicClass

// Internal visibility (same module)
internal class InternalClass

// Private class (same file)
private class PrivateClass

class Example {
    // Public property
    val publicProperty = "public"
    
    // Internal property
    internal val internalProperty = "internal"
    
    // Protected property (visible to subclasses)
    protected val protectedProperty = "protected"
    
    // Private property
    private val privateProperty = "private"
    
    // Public method
    fun publicMethod() {}
    
    // Private method
    private fun privateMethod() {}
}

Interfaces

Interface Definition and Implementation

kotlin
// Interface definition
interface Drawable {
    // Abstract property
    val color: String
    
    // Abstract method
    fun draw()
    
    // Method with default implementation
    fun getInfo(): String = "Drawable object with color $color"
    
    // Property with default implementation
    val isVisible: Boolean
        get() = true
}

// Implementing an interface
class Circle(override val color: String, val radius: Double) : Drawable {
    override fun draw() {
        println("Drawing a $color circle with radius $radius")
    }
    
    override fun getInfo(): String {
        return "Circle: ${super.getInfo()}, radius=$radius"
    }
}

// Multiple interface implementation
interface Movable {
    fun move(x: Int, y: Int)
}

class MovableCircle(
    override val color: String,
    val radius: Double
) : Drawable, Movable {
    private var x = 0
    private var y = 0
    
    override fun draw() {
        println("Drawing circle at ($x, $y)")
    }
    
    override fun move(x: Int, y: Int) {
        this.x = x
        this.y = y
    }
}

Functional Interfaces (SAM)

kotlin
// Functional interface
fun interface StringProcessor {
    fun process(input: String): String
}

// Using lambda to implement
fun main() {
    val upperCaseProcessor = StringProcessor { it.uppercase() }
    val result = upperCaseProcessor.process("hello")
    println(result)  // HELLO
    
    // Pass lambda directly
    processString("world") { it.reversed() }
}

fun processString(input: String, processor: StringProcessor): String {
    return processor.process(input)
}

Object Declarations and Expressions

Object Declaration (Singleton)

kotlin
// Singleton object
object DatabaseManager {
    private var isConnected = false
    
    fun connect() {
        if (!isConnected) {
            println("Connecting to database...")
            isConnected = true
        }
    }
    
    fun disconnect() {
        if (isConnected) {
            println("Disconnecting from database...")
            isConnected = false
        }
    }
    
    fun isConnected() = isConnected
}

// Using singleton object
fun main() {
    DatabaseManager.connect()
    println("Connected: ${DatabaseManager.isConnected()}")
    DatabaseManager.disconnect()
}

Companion Objects

kotlin
class User private constructor(val name: String, val email: String) {
    companion object Factory {
        private var userCount = 0
        
        // Factory method
        fun createUser(name: String, email: String): User {
            userCount++
            return User(name, email)
        }
        
        fun createGuestUser(): User {
            return createUser("Guest", "guest@example.com")
        }
        
        // Companion object property
        fun getUserCount() = userCount
    }
    
    override fun toString() = "User(name='$name', email='$email')"
}

fun main() {
    val user1 = User.createUser("Alice", "alice@example.com")
    val user2 = User.createGuestUser()
    
    println("Users created: ${User.getUserCount()}")
    println(user1)
    println(user2)
}

Object Expressions (Anonymous Objects)

kotlin
interface ClickListener {
    fun onClick()
    fun onLongClick()
}

fun main() {
    // Anonymous object implementing interface
    val button = object : ClickListener {
        override fun onClick() {
            println("Button clicked")
        }
        
        override fun onLongClick() {
            println("Button long clicked")
        }
    }
    
    button.onClick()
    button.onLongClick()
    
    // Anonymous object extending a class
    val customList = object : ArrayList<String>() {
        override fun add(element: String): Boolean {
            println("Adding element: $element")
            return super.add(element)
        }
    }
    
    customList.add("Hello")
    customList.add("World")
}

Enum Classes

Basic Enum

kotlin
enum class Direction {
    NORTH, SOUTH, EAST, WEST
}

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF),
    YELLOW(0xFFFF00);
    
    fun getHexString(): String = "#${rgb.toString(16).padStart(6, '0')}"
}

fun main() {
    // Using enum
    val direction = Direction.NORTH
    println("Direction: $direction")
    
    // Enum properties and methods
    val red = Color.RED
    println("Red RGB: ${red.rgb}")
    println("Red Hex: ${red.getHexString()}")
    
    // Enum iteration
    println("All colors:")
    for (color in Color.values()) {
        println("${color.name}: ${color.getHexString()}")
    }
}

Advanced Enum Features

kotlin
enum class Planet(val mass: Double, val radius: Double) {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6),
    MARS(6.421e+23, 3.3972e6);
    
    // Methods in enum class
    fun surfaceGravity(): Double {
        val G = 6.67300E-11
        return G * mass / (radius * radius)
    }
    
    fun surfaceWeight(otherMass: Double): Double {
        return otherMass * surfaceGravity()
    }
    
    companion object {
        fun findByMass(mass: Double): Planet? {
            return values().find { it.mass == mass }
        }
    }
}

fun main() {
    val earthWeight = 175.0
    val mass = earthWeight / Planet.EARTH.surfaceGravity()
    
    println("Weight on different planets:")
    for (planet in Planet.values()) {
        val weight = planet.surfaceWeight(mass)
        println("${planet.name}: %.2f".format(weight))
    }
}

Sealed Classes

Sealed Class Definition

kotlin
// Sealed class represents a restricted class hierarchy
sealed class Result<out T>

data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
object Loading : Result<Nothing>()

// Using sealed class
fun handleResult(result: Result<String>) {
    when (result) {
        is Success -> println("Success: ${result.data}")
        is Error -> println("Error: ${result.exception.message}")
        Loading -> println("Loading...")
        // No else branch needed, compiler knows all possible subclasses
    }
}

fun main() {
    val results = listOf(
        Success("Data loaded successfully"),
        Error(RuntimeException("Network error")),
        Loading
    )
    
    results.forEach { handleResult(it) }
}

Sealed Interfaces

kotlin
sealed interface UIEvent

data class ButtonClick(val buttonId: String) : UIEvent
data class TextInput(val text: String) : UIEvent
object ScreenLoad : UIEvent

class UIEventHandler {
    fun handle(event: UIEvent) {
        when (event) {
            is ButtonClick -> println("Button ${event.buttonId} clicked")
            is TextInput -> println("Text entered: ${event.text}")
            ScreenLoad -> println("Screen loaded")
        }
    }
}

Data Classes

Data Class Definition

kotlin
// Data class automatically generates equals, hashCode, toString, copy, etc.
data class Person(
    val name: String,
    val age: Int,
    val email: String
) {
    // Data class can also have custom methods
    fun isAdult(): Boolean = age >= 18
    
    // Data class can have secondary constructors
    constructor(name: String, age: Int) : this(name, age, "")
}

fun main() {
    val person1 = Person("Alice", 25, "alice@example.com")
    val person2 = Person("Alice", 25, "alice@example.com")
    val person3 = person1.copy(age = 26)  // Copy with modification
    
    // Auto-generated methods
    println("person1 == person2: ${person1 == person2}")  // true
    println("person1: $person1")  // Auto-generated toString
    
    // Destructuring declaration
    val (name, age, email) = person1
    println("Name: $name, Age: $age, Email: $email")
    
    // Custom method
    println("Is adult: ${person1.isAdult()}")
}

Inline Classes (Value Classes)

kotlin
// Inline class provides type-safe wrapper with no runtime overhead
@JvmInline
value class UserId(val value: String)

@JvmInline
value class Email(val value: String) {
    init {
        require(value.contains("@")) { "Invalid email format" }
    }
    
    fun getDomain(): String = value.substringAfter("@")
}

fun processUser(userId: UserId, email: Email) {
    println("Processing user ${userId.value} with email ${email.value}")
    println("Email domain: ${email.getDomain()}")
}

fun main() {
    val userId = UserId("user123")
    val email = Email("user@example.com")
    
    processUser(userId, email)
    
    // Type safety: cannot mix up parameters
    // processUser(email, userId)  // Compilation error
}

Nested and Inner Classes

Nested Classes

kotlin
class Outer {
    private val outerProperty = "Outer property"
    
    // Nested class (static)
    class Nested {
        fun doSomething() {
            println("Nested class method")
            // println(outerProperty)  // Compilation error: cannot access outer class members
        }
    }
    
    // Inner class
    inner class Inner {
        fun doSomething() {
            println("Inner class method")
            println("Accessing: $outerProperty")  // Can access outer class members
        }
    }
}

fun main() {
    // Create nested class instance
    val nested = Outer.Nested()
    nested.doSomething()
    
    // Create inner class instance
    val outer = Outer()
    val inner = outer.Inner()
    inner.doSomething()
}

Extensions

Extension Functions

kotlin
// Add extension function to String class
fun String.isPalindrome(): Boolean {
    val cleaned = this.lowercase().replace(" ", "")
    return cleaned == cleaned.reversed()
}

// Add extension function to List
fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null

// Extension property
val String.wordCount: Int
    get() = this.split("\\s+".toRegex()).size

fun main() {
    val text = "A man a plan a canal Panama"
    println("'$text' is palindrome: ${text.isPalindrome()}")
    println("Word count: ${text.wordCount}")
    
    val numbers = listOf(1, 2, 3, 4, 5)
    println("Second element: ${numbers.secondOrNull()}")
}

Extension Scope

kotlin
class Host(val hostname: String) {
    fun printHostname() { print(hostname) }
}

class Connection(val host: Host, val port: Int) {
    fun printPort() { print(port) }
    
    fun Host.printConnectionString() {
        printHostname()   // Calls Host.printHostname()
        print(":")
        printPort()       // Calls Connection.printPort()
    }
    
    fun connect() {
        host.printConnectionString()  // Call extension function
    }
}

fun main() {
    Connection(Host("kotl.in"), 443).connect()
}

Delegation

Class Delegation

kotlin
interface Base {
    fun print()
    fun printMessage(message: String)
}

class BaseImpl(val x: Int) : Base {
    override fun print() { println("BaseImpl: $x") }
    override fun printMessage(message: String) { println("BaseImpl: $message") }
}

// Delegate to another object
class Derived(b: Base) : Base by b {
    // Can override delegated methods
    override fun printMessage(message: String) {
        println("Derived: $message")
    }
}

fun main() {
    val base = BaseImpl(42)
    val derived = Derived(base)
    
    derived.print()  // Delegates to BaseImpl
    derived.printMessage("Hello")  // Uses overridden method
}

Best Practices

1. Package Organization

kotlin
// Organize packages by feature, not technical layer
// Good organization
com.example.ecommerce/
├── user/
├── product/
├── order/
└── payment/

// Avoid this organization
com.example.ecommerce/
├── controllers/
├── services/
├── repositories/
└── models/

2. Class Design

kotlin
// Prefer data classes
data class User(val id: Long, val name: String, val email: String)

// Use sealed classes for state
sealed class LoadingState {
    object Loading : LoadingState()
    data class Success(val data: String) : LoadingState()
    data class Error(val message: String) : LoadingState()
}

// Use object declarations for singletons
object ConfigManager {
    fun getConfig(key: String): String? = TODO()
}

3. Visibility

kotlin
class ApiClient {
    // Expose necessary interfaces
    fun fetchData(): String = processRequest()
    
    // Hide implementation details
    private fun processRequest(): String = TODO()
    
    // Use internal to limit access within module
    internal fun debugInfo(): String = TODO()
}

Next Steps

After understanding program structure, let's dive into Kotlin's data type system.

Next Chapter: Data Types

Exercises

  1. Create a project structure with multiple packages
  2. Implement a shape drawing system using interfaces and implementation classes
  3. Design a network request result using sealed classes to represent different states
  4. Create a factory pattern example using companion objects
  5. Implement a utility library that enhances existing classes using extension functions

Content is for learning and research only.