61. What is the difference between init and constructor in Kotlin?
Feature | constructor | init block |
---|---|---|
Purpose | Declares how to create an object | Executes initialization logic after construction |
Called | Automatically on object creation | Automatically after primary constructor |
Syntax location | In class header or body | Inside class body |
Frequency | Can only declare one primary constructor | Can have multiple init blocks |
Use case | Pass parameters, assign properties | Validation, logging, computed setup |
62. The concept of generics in Kotlin.
Generics in Kotlin allow you to write type-safe and reusable code that can work with any data type, while still retaining full compile-time type checking.
Basic Syntax:
class Box<T>(val value: T) {
fun get(): T = value
}
Here, T is a type parameter, which can be replaced with any actual type (e.g., Box<Int>, Box<String>).
Generic Functions
fun <T> printList(items: List<T>) {
for (item in items) println(item)
}
printList(listOf(1, 2, 3))
printList(listOf("a", "b", "c"))
*Importants:
Feature | Syntax | Meaning |
---|---|---|
Type parameter | <T> | A placeholder for a type |
Constraint | <T : SomeType> | T must be a subtype of SomeType |
Covariance | out T | Produces values of type T |
Contravariance | in T | Consumes values of type T |
Star projection | * | Unknown type |
63. Difference Between Invariance, Covariance, and Contravariance in Kotlin Generics
val listOfStrings: MutableList<String> = mutableListOf()
val listOfAny: MutableList<Any> = listOfStrings // ❌ Compilation error!
Example:
interface Producer<out T> {
fun produce(): T
}
val producerString: Producer<String> = object : Producer<String> {
override fun produce() = "Hello"
}
val producerAny: Producer<Any> = producerString // ✅ Allowed because of covariance
Example:
interface Consumer<in T> {
fun consume(item: T)
}
val consumerAny: Consumer<Any> = object : Consumer<Any> {
override fun consume(item: Any) {
println(item)
}
}
val consumerString: Consumer<String> = consumerAny // ✅ Allowed because of contravariance
Variance | Keyword | Meaning | Usage Context | Example |
---|---|---|---|---|
Invariance | (none) | Exact type match required | Both input & output | MutableList<T> |
Covariance | out | Subtype preserved (Producer) | Output only | List<out T> |
Contravariance | in | Subtype reversed (Consumer) | Input only | Comparable<in T> |
64. The concept of typealias in Kotlin.
typealias in Kotlin allows you to give an alternative name to an existing type. It can improve code readability, simplify long type declarations, or create more meaningful names for complex types.
It does not create a new type — it's just a shortcut or an alias to an existing type.
Syntax
typealias NewName = ExistingType
Examples
Alias for a generic type
typealias UserMap = Map<String, User>
Now, instead of writing Map<String, User>, you can write:
val users: UserMap = mapOf("id1" to User("Alice"), "id2" to User("Bob"))
65. The concept of tail recursion in Kotlin
Tail recursion is a special form of recursion where the recursive call is the last operation in a function. This allows the compiler to optimize the recursion by reusing the same stack frame instead of creating a new one for each call — preventing stack overflow and improving performance.
A function is tail-recursive if:
Kotlin supports tail recursion via the tailrec modifier.
If a function is marked tailrec and is properly tail-recursive, the compiler will optimize it into a loop internally.
Example: Factorial Using Tail Recursion
tailrec fun factorial(n: Int, accumulator: Long = 1): Long {
return if (n <= 1) accumulator
else factorial(n - 1, accumulator * n) // recursive call is last operation
}
Calling:
println(factorial(5)) // Output: 120
1. Without tailrec, recursive calls can cause stack overflow for large inputs.
2. With tailrec, Kotlin transforms recursion into a loop under the hood, so it’s safe for large input values.
Feature | Description |
---|---|
Tail Recursion | Recursive call is last operation |
Kotlin Keyword | tailrec modifier |
Benefit | Prevents stack overflow, improves performance |
Compiler Optimization | Converts recursion into a loop |