Kotlin Functions

Declare reusable code blocks with parameters, return types, defaults, recursion, and inline optimization

fun Parameters Recursion inline

Table of Contents

1Functions

A function is a reusable block of code that performs a specific task. In Kotlin, functions are declared with the fun keyword and can exist at the top level (outside any class) — unlike Java where methods must belong to a class.

Basic function syntax

Function declarationfun greet(name: String): String { return "Hello, $name!" } fun main() { val message = greet("Nikhil") println(message) // Hello, Nikhil! }

Top-level vs member functions

TypeLocationExample
Top-levelDirectly in a .kt filefun main(), utility helpers
MemberInside a class or objectclass User { fun display() }
ExtensionOutside class, extends typefun String.capitalizeWords()
Top-level and member functions// Top-level function fun add(a: Int, b: Int) = a + b class Calculator { // Member function fun multiply(a: Int, b: Int): Int { return a * b } } fun main() { println(add(5, 3)) // 8 println(Calculator().multiply(4, 2)) // 8 }
Single-expression functions

When a function body is a single expression, use = expression instead of { return ... }. The return type can be inferred automatically.

2Parameters

Parameters are variables listed in the function signature. Each parameter has a name and a type. Kotlin supports multiple parameters, vararg, and no parameters at all.

Multiple parameters

Multiple parametersfun printUserInfo(name: String, age: Int, city: String) { println("Name: $name, Age: $age, City: $city") } fun main() { printUserInfo("Alex", 22, "Hyderabad") }

vararg — variable number of arguments

varargfun sum(vararg numbers: Int): Int { return numbers.sum() } fun main() { println(sum(1, 2, 3)) // 6 println(sum(10, 20, 30, 40)) // 100 val scores = intArrayOf(85, 90, 78) println(sum(*scores)) // spread operator * }

Parameter rules

RuleDescription
Type requiredEvery parameter must have an explicit type
One vararg maxOnly one vararg per function; usually last
ImmutabilityParameters are val — cannot be reassigned
Spread operator*array unpacks array into vararg arguments
No parameters: Use empty parentheses — fun sayHello() { println("Hi!") }

3Return Types

Functions that return a value must declare a return type after the parameter list. Functions that return nothing use Unit (similar to Java's void).

Explicit return typefun square(n: Int): Int { return n * n } fun logMessage(msg: String): Unit { // Unit can be omitted println("[LOG] $msg") } fun main() { println(square(5)) // 25 logMessage("Started") }

Single-expression return (type inferred)

Expression bodyfun max(a: Int, b: Int) = if (a > b) a else b fun isEven(n: Int): Boolean = n % 2 == 0 fun fullName(first: String, last: String) = "$first $last" fun main() { println(max(10, 7)) // 10 println(isEven(4)) // true println(fullName("N", "K")) // N K }

Return type reference

Return typeMeaningExample
Int, String, etc.Returns a valuefun getAge(): Int
UnitNo meaningful return (optional to write)fun printLine(): Unit
NothingNever returns (throws or infinite loop)fun fail(): Nothing
NullableMay return nullfun find(): String?
Early return

Use return anywhere in the function body to exit early — useful in validation and guard clauses.

4Default Arguments

Kotlin lets you assign default values to parameters. Callers can omit those arguments and the default is used — reducing overloaded method duplication common in Java.

Default parameter valuesfun greet(name: String, greeting: String = "Hello", punctuation: String = "!") { println("$greeting, $name$punctuation") } fun main() { greet("Nikhil") // Hello, Nikhil! greet("Alex", "Hi") // Hi, Alex! greet("Sam", greeting = "Welcome") // Welcome, Sam! }

Kotlin vs Java overloading

ApproachJavaKotlin
Multiple signaturesOverload methods with different paramsSingle function with defaults
Exampleconnect(), connect(host), connect(host, port)connect(host = "localhost", port = 8080)
Practical default — API callfun fetchData( url: String, timeout: Int = 30, retries: Int = 3, useCache: Boolean = true ) { println("GET $url | timeout=$timeout, retries=$retries, cache=$useCache") } fun main() { fetchData("https://api.example.com/users") fetchData("https://api.example.com/posts", timeout = 60) }
Order rule: Parameters without defaults cannot follow parameters with defaults. Put required parameters first, optional ones last.

5Named Arguments

Named arguments let you pass values by parameter name instead of position. This improves readability when a function has many parameters of the same type or when skipping defaults in the middle.

Named argumentsfun createProfile( name: String, age: Int, email: String = "", isActive: Boolean = true ) { println("Profile: $name, $age, $email, active=$isActive") } fun main() { createProfile(name = "Nikhil", age = 25) createProfile(age = 30, name = "Alex", isActive = false) createProfile(name = "Sam", age = 22, email = "sam@mail.com") }

When named arguments help

Readability

send(to = user, subject = "Hi", body = msg) is clearer than positional args.

Any order

Pass arguments in any order when all are named.

Skip defaults

Override only the params you need without filling every slot.

Compose / UI

Jetpack Compose APIs rely heavily on named parameters.

Mixed positional and namedfun log(level: String, message: String, timestamp: Boolean = false) { val prefix = if (timestamp) "[${System.currentTimeMillis()}] " else "" println("${prefix}[$level] $message") } fun main() { log("INFO", message = "App started") // positional + named log(level = "ERROR", message = "Failed", timestamp = true) }
Rule

Once you use a named argument, all following arguments must also be named. Positional arguments must come before named ones.

6Recursion

Recursion occurs when a function calls itself to solve a problem by breaking it into smaller subproblems. Every recursive function needs a base case to stop the chain of calls.

Factorial (recursive)fun factorial(n: Int): Long { if (n <= 1) return 1L // base case return n * factorial(n - 1) // recursive call } fun main() { println(factorial(5)) // 120 println(factorial(0)) // 1 }

Fibonacci sequence

Fibonaccifun fibonacci(n: Int): Int { if (n <= 1) return n return fibonacci(n - 1) + fibonacci(n - 2) } fun main() { for (i in 0..7) { print("${fibonacci(i)} ") } // 0 1 1 2 3 5 8 13 }

Tail recursion

Kotlin optimizes tail-recursive functions (recursive call is the last operation) with the tailrec modifier — converting recursion to a loop at compile time to avoid stack overflow.

tailrec factorialtailrec fun factorialTail(n: Int, accumulator: Long = 1): Long { if (n <= 1) return accumulator return factorialTail(n - 1, n * accumulator) } fun main() { println(factorialTail(20)) // efficient for large n }
ConceptDescription
Base caseCondition that stops recursion (e.g. n <= 1)
Recursive caseFunction calls itself with a smaller input
Stack overflowToo many calls without base case — use tailrec or loops
tailrecMust call itself as the final operation only
When to use loops instead: Simple iteration (sum, search) is often clearer and faster with for or while — see Control Statements.

7Inline Functions

An inline function is marked with the inline keyword. The compiler copies the function body at each call site instead of creating a lambda object — reducing overhead when passing functions as parameters (higher-order functions).

Basic inline functioninline fun measureTime(block: () -> Unit) { val start = System.currentTimeMillis() block() val elapsed = System.currentTimeMillis() - start println("Elapsed: ${elapsed}ms") } fun main() { measureTime { Thread.sleep(100) println("Work done") } }

Why use inline?

BenefitExplanation
PerformanceAvoids object allocation for lambdas on each call
Reified genericsinline fun <T> with reified T — access type at runtime
Non-local returnsLambda can return from enclosing function when inlined

Reified type parameters

reified — runtime type accessinline fun <reified T> isInstance(value: Any): Boolean { return value is T } fun main() { println(isInstance<String>("hello")) // true println(isInstance<Int>("hello")) // false }

noinline and crossinline

ModifierPurpose
noinlineExclude a lambda parameter from inlining (pass to another function)
crossinlineLambda must not use non-local return; can be called from nested context
Standard library examples// These are inline in Kotlin stdlib: listOf(1, 2, 3).filter { it > 1 } // filter is inline listOf(1, 2, 3).map { it * 2 } // map is inline run { println("block") } // run is inline
When NOT to inline

Avoid inline on large functions — code duplication increases bytecode size. Use inline mainly for small higher-order functions. Learn more in Lambda & Higher-Order Functions.

8Summary Cheatsheet

TopicKey Takeaway
Functionsfun name(params): ReturnType { body }; top-level allowed
ParametersTyped, immutable; vararg for variable args; spread with *
Return TypesExplicit or inferred; Unit = void; single-expression =
Default Argumentsparam: Type = value — reduces Java-style overloads
Named Argumentsfn(name = "x", age = 10) — any order, skip defaults
RecursionBase case required; tailrec for optimized tail calls
Inline Functionsinline fun — no lambda object; enables reified
Next lessonNull Safety — nullable types and safe calls

Kotlin Functions MCQ Practice

10 Basic MCQs 10 Advanced MCQs

10 Basic Kotlin Functions MCQs

1

Which keyword is used to declare a function in Kotlin?

Afunction
Bfun
Cdef
Dfunc
Explanation: Kotlin uses the fun keyword — e.g. fun greet(name: String): String.
2

Unlike Java, Kotlin allows functions to be declared where?

AAt the top level, outside any class
BOnly inside classes
COnly in companion objects
DOnly in interfaces
Explanation: Top-level functions like fun main() can live directly in a .kt file without a enclosing class.
3

What is Kotlin's equivalent of Java's void return type?

ANothing
BUnit
CVoid
DNull
Explanation: Unit means no meaningful return value; it can be omitted from the signature.
4

For a single-expression function, which syntax is idiomatic?

Afun add(a, b) { return a + b }
Bfun add(a, b): Int { a + b }
Cfun add(a: Int, b: Int) = a + b
Dadd = fun(a, b) -> a + b
Explanation: Use = expression for single-expression bodies; the return type is often inferred.
5

Which feature lets callers omit arguments and use predefined values?

ADefault parameter values
BNamed arguments only
Cvararg only
Dlateinit
Explanation: fun greet(name: String, greeting: String = "Hello") reduces Java-style overload duplication.
6

When all arguments at a call site are named, you can pass them in?

AOnly declaration order
BAny order
CReverse order only
DAlphabetical order only
Explanation: Named arguments improve readability and let you reorder parameters — e.g. createProfile(age = 30, name = "Alex").
7

Which modifier accepts a variable number of arguments of the same type?

Avararg
Bspread
Cvar
Ddynamic
Explanation: fun sum(vararg numbers: Int) packs multiple values; only one vararg per function is typical.
8

Which modifier tells the compiler to optimize tail-recursive calls into a loop?

Ainline
Btailrec
Csuspend
Dreified
Explanation: tailrec requires the recursive call to be the last operation — avoiding stack overflow on deep recursion.
9

inline functions are especially useful when?

APassing lambdas to higher-order functions
BDeclaring data classes
CImporting packages
DUsing sealed classes
Explanation: Inlining copies the lambda body at the call site, avoiding extra object allocation for small HOFs like filter and map.
10

reified type parameters require the function to be?

Asuspend
Bprivate
Cinline
Dopen
Explanation: inline fun <reified T> preserves generic type info at runtime for checks like value is T.

10 Advanced Kotlin Functions MCQs

11

Extension functions add behavior without?

AInheriting or modifying the original class
BUsing the fun keyword
CDeclaring parameters
DReturning a value
Explanation: fun String.capitalizeWords() extends a type statically — no subclass or inheritance required.
12

In an inline function with multiple lambda parameters, noinline means?

AInline all lambdas
BDo not inline that lambda parameter
CMake the lambda reified
DForce non-local return
Explanation: noinline keeps a lambda as a real object — useful when passing it to another non-inline function.
13

crossinline on a lambda parameter means?

AThe lambda cannot use non-local return
BThe lambda must be reified
CThe lambda is always inlined twice
DThe lambda becomes nullable
Explanation: crossinline restricts non-local returns when the lambda is invoked from nested inline contexts.
14

To expose Java-friendly overloads from Kotlin default parameters, annotate with?

A@JvmStatic
B@JvmOverloads
C@JvmField
D@Suppress
Explanation: @JvmOverloads generates overloaded methods for Java callers who cannot use default arguments.
15

Every recursive function must include?

AA base case that stops recursion
BAt least three parameters
CA vararg parameter
DAn inline modifier
Explanation: Without a base case (e.g. if (n <= 1) return 1), recursion never terminates and causes stack overflow.
16

In fun double(x: Int) = x * 2, the return type is?

AUnit
BInferred as Int
CNothing
DMust always be written explicitly
Explanation: Single-expression functions infer the return type from the expression — here Int.
17

Kotlin function parameters are treated as?

AImmutable (val) — cannot be reassigned
BMutable (var) by default
COptional without a type
DAlways nullable
Explanation: Parameters behave like val locals; reassigning a parameter is a compile error.
18

To pass an array to a vararg parameter, use?

AThe spread operator *
BThe range operator ..
C@array
Dspread keyword
Explanation: sum(*scores) unpacks an IntArray into individual vararg arguments.
19

Functions declared inside another function are?

ANot supported in Kotlin
BOnly for Java interop
CAllowed as local/nested functions
DOnly in interfaces
Explanation: Local functions encapsulate helper logic and can access variables from the enclosing scope.
20

Inlining very large functions can?

AAlways shrink APK size
BIncrease bytecode size due to code duplication
CDisable reified generics
DRemove all lambdas from the project
Explanation: Each call site gets a copy of the body — fine for small HOFs, costly for large implementations.
Click an option to select, then check answers.

Kotlin Functions Interview Q&A

15 topic-focused questions for interviews and revision.

1How do you declare a basic Kotlin function with parameters and a return type?easy
Answer: Use fun name(param: Type): ReturnType { body } — e.g. fun greet(name: String): String { return "Hello, $name" }. Parameters require explicit types.
2What is the difference between top-level and member functions?easy
Answer: Top-level functions live directly in a file (like fun main()). Member functions belong to a class or object. Both use the fun keyword.
3Explain Unit and when you omit it.easy
Answer: Unit is like Java void — the function returns no meaningful value. You can omit : Unit from the signature when there is no return value.
4What are default arguments and how do they help?medium
Answer: Parameters can have defaults: fun connect(host: String = "localhost", port: Int = 8080). Callers omit arguments to use defaults, replacing multiple Java overloads with one function.
5When should you use named arguments?easy
Answer: When a function has many parameters, similar types, or defaults you want to skip — named args improve readability and allow any order when all are named.
6How does vararg work and what is the spread operator?medium
Answer: vararg packs multiple values into an array inside the function. Use *array at the call site to expand an existing array into separate arguments.
7What is a single-expression function?easy
Answer: fun max(a: Int, b: Int) = if (a > b) a else b uses = instead of a block with return. Return type is inferred from the expression.
8Explain recursion and the need for a base case.medium
Answer: A function calls itself with a smaller problem until a base case stops recursion — e.g. factorial returns 1 when n <= 1. Without a base case, stack overflow occurs.
9What is tailrec and when should you use it?hard
Answer: tailrec marks tail-recursive functions where the recursive call is the last operation. The compiler rewrites it as a loop, avoiding deep call stacks for large inputs.
10Why use inline functions?medium
Answer: inline copies the function (and lambda) body at each call site, avoiding lambda object allocation overhead in higher-order functions and enabling non-local returns from lambdas.
11What are reified type parameters?hard
Answer: With inline fun <reified T>, generic type T is available at runtime for checks like value is T. Normally type erasure removes generic info on the JVM.
12Difference between noinline and crossinline?hard
Answer: noinline prevents inlining a specific lambda (so it can be stored or passed elsewhere). crossinline allows inlining but forbids non-local return from that lambda.
13How do extension functions differ from inheritance?medium
Answer: Extensions add functions to existing types without subclassing or modifying source. They are resolved statically at compile time based on the declared receiver type.
14What does @JvmOverloads do?medium
Answer: It generates Java overload methods for each combination of default parameters so Java code can call Kotlin functions without named/default argument support.
15When should you avoid inlining a function?hard
Answer: Avoid inline on large functions — bytecode grows because the body is duplicated at every call site. Reserve inlining for small higher-order utilities.