Kotlin Functions: Parameters, Default/Named Arguments, Vararg, Single Expression & Local Functions

1. Defining Functions and Parameters

How do you define functions and parameters in Kotlin?

Functions in Kotlin are defined using the fun keyword, with optional parameters and return types. Parameters are typed values passed to functions, requiring explicit type annotations unless inferred. Return types are optional if inferred and use Unit for void equivalents.

Syntax:

fun functionName(param1: Type1, param2: Type2): ReturnType {
    // Function body
    return value
}

Use Case: Encapsulating reusable logic (e.g., calculations).

Example:

// Defining and calling functions
fun greet(name: String): String {
    return "Hello, $name!"
}

fun calculateSum(a: Int, b: Int): Int {
    return a + b
}

// Calling functions
fun main() {
    val message = greet("Krishna")
    println(message)
    
    val sum = calculateSum(5, 3)
    println("Sum: $sum")
}
Output:
Hello, Krishna!
Sum: 8

Note: greet returns a string using string interpolation ($name). calculateSum demonstrates typed parameters and return type. Run with: kotlinc basic_functions.kt -include-runtime -d basic_functions.jar && java -jar basic_functions.jar.

2. Default and Named Arguments

What are default and named arguments in Kotlin?

Default Arguments: Parameters with predefined values, making them optional during calls. Syntax: fun func(param: Type = defaultValue).

Named Arguments: Parameters passed by name, allowing flexible order and skipping defaults. Syntax: func(param1 = value1, param2 = value2).

Use Case: Simplifying function calls for optional parameters (e.g., configuration functions).

Example:

// Default and named arguments
fun calculateBonus(salary: Int, rate: Double = 0.1, bonusType: String = "Standard"): Double {
    val bonusRate = if (bonusType == "Premium") rate * 1.5 else rate
    return salary * bonusRate
}

fun main() {
    // Using defaults
    val standardBonus = calculateBonus(50000)
    println("Standard bonus: $standardBonus")
    
    // Named arguments (out of order)
    val premiumBonus = calculateBonus(salary = 60000, bonusType = "Premium")
    println("Premium bonus: $premiumBonus")
    
    // Mixing named and positional
    val customBonus = calculateBonus(55000, bonusType = "Standard")
    println("Custom bonus: $customBonus")
}
Output:
Standard bonus: 5000.0
Premium bonus: 9000.0
Custom bonus: 5500.0

Note: rate defaults to 0.1; bonusType to "Standard". Named arguments allow skipping or reordering.

3. Single Expression Functions

What are single expression functions in Kotlin?

Single expression functions are concise functions with a single expression as the body, using = instead of curly braces and return. The expression's value is returned automatically.

Syntax: fun func(param: Type): ReturnType = expression.

Use Case: Concise functions for simple calculations or transformations.

Example:

// Single expression functions
fun square(x: Int): Int = x * x

fun isEven(num: Int): Boolean = num % 2 == 0

fun greet(name: String, greeting: String = "Hello") = "$greeting, $name!"

fun main() {
    println("Square of 5: ${square(5)}")
    println("Is 4 even? ${isEven(4)}")
    println(greet("Krishna"))
    println(greet("Ram", "Hi"))
}
Output:
Square of 5: 25
Is 4 even: true
Hello, Krishna!
Hi, Ram!

Note: square and isEven return the expression's result directly. greet combines default and single-expression syntax.

4. Variable Number of Arguments (vararg)

What are variable number of arguments (vararg) in Kotlin?

vararg allows a function to accept a variable number of arguments, treated as an array. Arguments can be passed individually or unpacked from arrays using the spread operator (*).

Syntax: fun func(vararg args: Type).

Use Case: Functions with dynamic argument counts (e.g., sum, logging).

Example:

// Variable number of arguments (vararg)
fun sum(vararg numbers: Int): Int {
    var total = 0
    for (num in numbers) {
        total += num
    }
    return total
}

fun printMessages(vararg messages: String) {
    for (msg in messages) {
        println(msg)
    }
}

fun main() {
    println("Sum: ${sum(1, 2, 3, 4)}")
    println("Sum with array: ${sum(*arrayOf(5, 6, 7))}")
    printMessages("Hello", "World", "Kotlin")
}
Output:
Sum: 10
Sum with array: 18
Hello
World
Kotlin

Note: sum sums variable integers; *arrayOf spreads an array into arguments. printMessages prints variable strings.

5. Local Functions

What are local functions in Kotlin?

Local functions are functions defined inside another function, with access to the outer function's scope. They help encapsulate helper logic without polluting the global namespace.

Syntax:

fun outerFunction(param: Type) {
    fun localFunction(): ReturnType {
        // Can access param
        return param * 2
    }
    return localFunction()
}

Use Case: Helper functions for specific outer function logic.

Example:

// Local functions example
fun processData(input: Int): Int {
    fun validate(num: Int): Boolean {
        return num > 0
    }
    
    fun calculate(num: Int): Int {
        if (!validate(num)) {
            return 0
        }
        return num * 2  // Uses input from outer scope
    }
    
    return calculate(input)
}

fun main() {
    println("Processed: ${processData(5)}")  // 10
    println("Processed: ${processData(0)}")  // 0
}
Output:
Processed: 10
Processed: 0

Note: validate and calculate are local to processData. calculate accesses input from the outer scope.

6. Comprehensive Example Combining All Function Concepts

Example:

// Comprehensive Kotlin functions example
fun processEmployee(name: String, salary: Double = 50000.0, vararg skills: String, dept: String = "IT", role: String = "Developer"): String {
    // Local function
    fun calculateBonus(years: Int): Double {
        return if (years >= 5) salary * 0.1 else salary * 0.05
    }
    
    // Single expression function
    val totalSalary = { years: Int -> salary + calculateBonus(years) }
    
    val bonus = calculateBonus(6)
    val total = totalSalary(6)
    
    return """
        Employee: $name
        Department: $dept
        Role: $role
        Skills: ${skills.joinToString()}
        Bonus: $${bonus}
        Total Salary: $${total}
    """.trimIndent()
}

fun main() {
    // Default arguments
    val result1 = processEmployee("Krishna")
    println(result1)
    
    // Named arguments
    val result2 = processEmployee("Ram", dept = "HR", role = "Manager")
    println(result2)
    
    // Vararg
    val result3 = processEmployee("Kristal", 60000.0, "Kotlin", "Java", "SQL", dept = "DevOps")
    println(result3)
}
Output:
Employee: Krishna
Department: IT
Role: Developer
Skills:
Bonus: $5000.0
Total Salary: $55000.0

Employee: Ram
Department: HR
Role: Manager
Skills:
Bonus: $5000.0
Total Salary: $55000.0

Employee: Kristal
Department: DevOps
Role: Developer
Skills: Kotlin, Java, SQL
Bonus: $6000.0
Total Salary: $66000.0

Description: processEmployee uses various argument types. Default/Named: salary, dept, role defaults; named for flexibility. Vararg: skills accepts variable strings. Local Function: calculateBonus for bonus calculation. Single Expression: totalSalary lambda. Uses string interpolation and joinToString for output.

7. Common Mistakes and Best Practices

Common Mistakes:

Best Practices: