Chapter 9: Functions – Part 2 (Advanced)
Functions – Part 2 (Advanced) — this is where Kotlin functions become really powerful, elegant, and very professional! ☕✨
We’re going to cover four super-important advanced function features that you will see everywhere in real Kotlin code (Android apps, backend services, libraries, etc.):
- Variable number of arguments (vararg)
- Infix functions (super clean syntax!)
- Local functions (functions inside functions)
- Tail-recursive functions (tailrec) (Kotlin’s safe recursion)
As always, imagine we’re sitting together in a quiet Mumbai café — I’ll explain everything very slowly, with real-life analogies, complete runnable examples, step-by-step breakdowns, tables, common mistakes with fixes, and fun facts so everything sticks perfectly.
Let’s dive in!
1. Variable Number of Arguments – vararg
vararg allows a function to accept any number of arguments of the same type — like print() or listOf().
Syntax: Put vararg before the parameter name.
|
0 1 2 3 4 5 6 7 8 9 10 |
fun greetAll(vararg names: String) { for (name in names) { println("नमस्ते $name!") } } |
Calling it
|
0 1 2 3 4 5 6 7 8 9 10 11 |
fun main() { greetAll("Webliance") // 1 argument greetAll("Amit", "Priya", "Rahul") // 3 arguments greetAll("Sachin", "Virat", "Rohit", "Shubman") // 4 arguments greetAll() // 0 arguments – OK! } |
Output:
|
0 1 2 3 4 5 6 7 8 9 10 |
नमस्ते Webliance! नमस्ते Amit! नमस्ते Priya! नमस्ते Rahul! ... |
Inside the function → names is treated as an array (Array<String>)
Very common pattern – combine with other parameters
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fun printTicket(movie: String, vararg seats: String) { println("Movie: $movie") println("Seats: ${seats.joinToString(", ")}") } fun main() { printTicket("RRR", "A12", "A13", "B5") // Movie: RRR // Seats: A12, A13, B5 } |
Important rule:
- vararg parameter should be last (or only one vararg allowed)
- Wrong: fun wrong(vararg a: Int, b: String) → compile error
- Correct: fun correct(b: String, vararg a: Int)
2. Infix Functions – Super Clean & Readable Syntax
Infix functions let you call a function without the dot and parentheses — like operators!
Rules to make a function infix:
- Must be a member function or extension function
- Must have exactly one parameter
- Must be marked with infix keyword
Real-life analogy: Normal: person.addFriend(friend) Infix: person addFriend friend → looks like natural language!
Example 1 – Custom infix operator
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Person(val name: String) { private val friends = mutableListOf<Person>() infix fun addFriend(friend: Person) { friends.add(friend) println("$name ने ${friend.name} ला मित्र म्हणून जोडलं!") } } fun main() { val amit = Person("Amit") val priya = Person("Priya") val rahul = Person("Rahul") amit addFriend priya // infix call – beautiful! amit addFriend rahul // Normal call also works priya.addFriend(amit) } |
Output:
|
0 1 2 3 4 5 6 7 |
Amit ने Priya ला मित्र म्हणून जोडलं! Amit ने Rahul ला मित्र म्हणून जोडलं! |
Example 2 – Infix with numbers (real-world style)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
infix fun Int.isMultipleOf(divisor: Int): Boolean = this % divisor == 0 fun main() { println(15 isMultipleOf 3) // true println(20 isMultipleOf 7) // false } |
Very common built-in infix functions:
- to → 1 to 10
- downTo → 10 downTo 1
- step → 1..10 step 2
- until → 1 until 10
3. Local Functions – Functions Inside Functions
You can declare functions inside other functions — very useful for helper logic that you don’t want to expose outside.
Real-life analogy: You’re cooking biryani → you have a main function (cookBiryani) Inside it, you have small helper functions (fryOnions, marinateChicken) that only make sense inside this recipe.
Example
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
fun cookBiryani() { println("Biryani बनवायला सुरुवात!") // Local function – only visible inside cookBiryani fun fryOnions() { println("कांदे सोनेरी होईपर्यंत परतवत आहे...") } fun marinateChicken() { println("चिकनला मसाले लावत आहे...") } fryOnions() marinateChicken() println("डब्यात गरमागरम बिर्याणी तयार!") } fun main() { cookBiryani() } |
Output:
|
0 1 2 3 4 5 6 7 8 9 |
Biryani बनवायला सुरुवात! कांदे सोनेरी होईपर्यंत परतवत आहे... चिकनला मसाले लावत आहे... डब्यात गरमागरम बिर्याणी तयार! |
Very common use: inside complex functions to break logic into small readable pieces.
4. Tail-Recursive Functions (tailrec) – Safe Recursion
Recursion in Kotlin is safe only if it’s tail-recursive — otherwise you get StackOverflowError for deep recursion.
tailrec keyword tells compiler: optimize this recursion into a loop (no stack growth!)
Example – Factorial (normal recursion vs tail-recursive)
Bad (can crash for large n):
|
0 1 2 3 4 5 6 7 8 9 |
fun factorial(n: Int): Long { if (n <= 1) return 1 return n * factorial(n - 1) // Not tail-recursive } |
Good (tail-recursive – safe & fast):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
tailrec fun factorial(n: Int, accumulator: Long = 1): Long { if (n <= 1) return accumulator return factorial(n - 1, n * accumulator) // Tail call – last operation } fun main() { println(factorial(20)) // Safe even for large numbers! } |
When can you use tailrec?
- The recursive call must be the last thing the function does
- No other work after the recursive call
Real-world use: tree traversal, list processing, etc.
Quick Recap Table (Your Cheat Sheet)
| Feature | Kotlin Way (Best Practice) | Example / Key Point |
|---|---|---|
| vararg | fun greet(vararg names: String) | Accepts any number of arguments |
| Infix function | infix fun addFriend(friend: Person) | Call without . & () → p addFriend f |
| Local function | fun outer() { fun inner() { … } } | Helper function visible only inside outer |
| Tail-recursive | tailrec fun factorial(n: Int, acc: Long = 1) | Safe recursion – compiler turns into loop |
Common Newbie Mistakes & Fixes
| Mistake | Problem | Fix |
|---|---|---|
| Putting vararg not as last parameter | Compile error | vararg must be the last parameter |
| Forgetting infix keyword | Cannot use infix call | Add infix before fun |
| Using return inside local function | Returns from outer function | Use return@label if needed |
| Adding code after recursive call | tailrec not allowed | Make sure recursive call is the last action |
| Not marking tail-recursive functions | StackOverflowError for large input | Add tailrec keyword |
Homework for You (Let’s Make It Fun!)
- Basic Create a function fun printAll(vararg messages: String) that prints all messages with numbers (1. Hello, 2. World…)
- Medium Create an infix function infix fun Person.likes(food: String) → prints “$name likes $food!”
- Advanced Write a tail-recursive function tailrec fun sum(n: Int, acc: Long = 0) that sums numbers from 1 to n.
- Fun Create a function fun makeSandwich(vararg ingredients: String) → prints a nice sandwich description.
- Challenge Fix this code so it works safely for large input:
Kotlin0123456789fun factorial(n: Int): Long {if (n <= 1) return 1return n * factorial(n - 1)}
You’ve just unlocked Kotlin’s most powerful function features — now your code is clean, expressive, and safe!
