11. Higher-order functions in Kotlin.
In Kotlin, higher-order functions are functions that either:
This is a powerful feature of Kotlin (and functional programming in general) that allows you to write more flexible and reusable code.
Example 1: Passing a function as a parameter
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// Usage
val sum = operate(5, 3) { x, y -> x + y }
val product = operate(5, 3) { x, y -> x * y }
println(sum) // Output: 8
println(product) // Output: 15
In this example, operate is a higher-order function that takes another function operation as a parameter.
Example 2: Returning a function
fun multiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}
// Usage
val timesTwo = multiplier(2)
println(timesTwo(4)) // Output: 8
Here, multiplier returns a function that multiplies its input by factor.
Real-world use: List operations
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
println(doubled) // Output: [2, 4, 6, 8, 10]
Functions like map, filter, forEach, and reduce are higher-order functions that accept lambdas to operate on collections.
Function | Description |
---|---|
map | Transforms each element and returns a new list. |
filter | Filters elements based on a predicate. |
forEach | Performs an action for each element (does not return anything). |
reduce | Accumulates a value by applying an operation from left to right. |
fold | Like reduce, but starts with an initial value. |
flatMap | Maps each element to a list and flattens the result. |
groupBy | Groups elements by a key selector. |
count | Counts elements matching a condition. |
any | Returns true if any element matches a predicate. |
all | Returns true if all elements match a predicate. |
none | Returns true if no element matches a predicate. |
take, drop | Select or skip first N elements. |
sortedBy | Sorts by a selector function. |
Function | Description |
---|---|
let | Executes a block with the object as it; returns the block result. |
apply | Configures an object (uses this); returns the object. |
also | Similar to let, but returns the object. |
run | Combines let and apply; returns the last expression. |
with | Similar to run, used with receiver object outside chain. |
12. The use of the lateinit modifier in Kotlin
The lateinit modifier in Kotlin is used to defer the initialization of a var (mutable) non-null property when you can’t initialize it at the point of declaration but you’re sure it will be initialized before use.
Syntax Example:
class User {
lateinit var name: String
fun initialize() {
name = "Alice"
}
fun printName() {
if (::name.isInitialized) {
println(name)
} else {
println("Name is not initialized")
}
}
}
13. Comparison between lateinit and lazy initialization
Feature | lateinit | lazy |
---|---|---|
Usage with | Only var, non-nullable types | Only val, immutable types |
Initialization time | Initialized later manually | Initialized automatically on first access |
Type restriction | Only non-null reference types | Works with any type including primitives |
Null safety | Not null-safe | Can be used with nullable and non-null types |
Common use cases | Android views, dependency injection, unit tests | Expensive operations (e.g., reading a file or database) |
Thread safety | Not thread-safe | Thread safety by default (can configure it) |
Exception if uninitialized | UninitializedPropertyAccessException if accessed before init | No such issue; it's always safe to access |
Example: lateinit:
class User {
lateinit var name: String
fun setName(newName: String) {
name = newName
}
fun printName() {
println(name)
}
}
Example: lazy:
val userInfo: String by lazy {
println("Initializing...")
"User: Alice"
}
fun main() {
println(userInfo) // "Initializing..." printed here, not before
}
14. What is a data class in Kotlin?
A data class in Kotlin is a special type of class designed to hold data. It automatically provides useful methods like:
Declaring a Data Class
data class User(val name: String, val age: Int)
Kotlin will auto-generate:
val user1 = User("Alice", 25)
println(user1) // User(name=Alice, age=25)
val user2 = user1.copy(age = 30) // copy with updated age
15. Difference between companion objects and static members
In Kotlin, companion objects are Kotlin's alternative to Java’s static members, since Kotlin does not have a static keyword. Here’s a clear comparison:
Concept | Companion Object (Kotlin) | Static Members (Java) |
---|---|---|
Syntax | companion object { ... } | static keyword |
Belongs to | A singleton object inside a class | Class itself |
Accessed via | ClassName.member | ClassName.member |
Instance required? | ❌ No instance needed | ❌ No instance needed |
Kotlin (Companion Object):
class MyClass {
companion object {
val staticLikeValue = "I am static-like"
fun staticLikeFunction() = "Hello from Kotlin"
}
}
val result = MyClass.staticLikeFunction()
Java (Static Members):
class MyClass {
static String staticValue = "I am static";
static String staticFunction() {
return "Hello from Java";
}
}
String result = MyClass.staticFunction();
Advanced Usage in Kotlin
class Factory {
companion object Builder {
fun create(): Factory = Factory()
}
}
Access:
val obj = Factory.create()
Summary:
Feature | Companion Object | Static Member (Java) |
---|---|---|
Static keyword | ❌ Not used | ✅ Yes |
Interface implementation | ✅ Allowed | ❌ Not allowed |
Interoperable with Java | ✅ via @JvmStatic | — |
Supports inheritance | ✅ | ❌ |