Kotlin Interview Questions
Master the most commonly asked interview questions with comprehensive, expert-crafted answers designed to help you succeed.
Explain about the “when” keyword in the context of Kotlin.
The when
keyword in Kotlin is a powerful control flow expression that acts as a substitute for the switch
statement found in languages like Java. It allows you to execute a block of code based on the value of a variable or expression. Each branch is checked sequentially until a match is found, and once a matching case is executed, the control flow exits the when
block. Unlike Java's switch
, Kotlin's when
does not require a break
statement at the end of each branch.
Example:
fun main(args: Array<String>) {
val temp = "Interview"
when(temp) {
"Interview" -> println("Interview Bit is the solution.")
"Job" -> println("Interview is the solution.")
"Success" -> println("Hard Work is the solution.")
}
}
Output:
Interview Bit is the solution.
Explanation: The variable temp
holds the value "Interview"
. The when
block compares this value against each branch, and upon finding a match, executes the corresponding statement. Since there's no need for a break
, the flow automatically exits after execution.
What are the advantages of Kotlin over Java?
Kotlin offers several advantages over Java that make it a preferred choice for modern Android and backend development:
- Data Classes: Kotlin reduces boilerplate code with
data
classes. In Java, you have to manually write getters, setters,equals()
,hashCode()
, andtoString()
methods, whereas Kotlin automatically generates them. - Concise Getters and Setters: In Kotlin, you don’t need to explicitly write getters and setters. If needed, Kotlin allows you to define custom getters and setters in a much more concise way than Java. It also supports property delegation for reusable patterns.
- Extension Functions: Kotlin allows developers to extend the functionality of existing classes with extension functions without having to inherit from them. Java lacks built-in support for this feature.
- Multi-platform Support: Kotlin supports multi-platform development, allowing developers to share code across Android, iOS, and web apps from a single codebase using Kotlin Multiplatform.
- Null Safety: Kotlin has built-in null safety through nullable and non-nullable types, which helps eliminate the common
NullPointerException
errors found in Java code. - Error Reduction: Kotlin’s concise and expressive syntax reduces the likelihood of errors and makes the code more readable and maintainable compared to Java.
What are the basic data types in Kotlin?
In Kotlin, the basic data types include:
- Numbers: Byte, Short, Int, Long, Float, Double
- Characters: Char
- Booleans: Boolean
- Strings: String
- Arrays: Array<T>, IntArray, FloatArray, etc.
Here is an example demonstrating usage of these types:
val age: Int = 25
val price: Double = 19.99
val grade: Char = 'A'
val isActive: Boolean = true
val name: String = "Kotlin"
val numbers: Array<Int> = arrayOf(1, 2, 3, 4)
Differentiate between open and public keywords in Kotlin.
In Kotlin, the open
and public
keywords serve different purposes, particularly in terms of inheritance and visibility.
- open: By default, classes and methods in Kotlin are
final
, meaning they cannot be inherited or overridden. Theopen
keyword allows a class or method to be extended or overridden. For example, marking a class or method asopen
means it is 'open for extension', similar to not marking a class asfinal
in Java. - public: This keyword defines visibility. In Kotlin, if no visibility modifier is explicitly declared,
public
is used by default. This means the declaration is accessible from any other code in the same module or project. It doesn’t affect inheritance or overriding, only how accessible the element is.
In summary, open
allows inheritance and overriding, while public
controls visibility. You need both if you want to allow external code to access and override a method.
What is Extension Function?
In Kotlin, an Extension Function allows developers to extend the functionality of an existing class without inheriting from it or using design patterns such as Decorator. This means you can add new methods to classes even if you don't have access to their source code.
To declare an extension function, you use the syntax where the function name is prefixed with the class you want to extend, followed by a dot:
fun <ClassName>.<MethodName>() {
// method body
}
For example, you can define a new function for the String
class like this:
fun String.printUpperCase() {
println(this.uppercase())
}
After this, any String
object can call printUpperCase()
as if it were a built-in method. Extension functions do not actually modify the classes they extend; instead, they are resolved statically at compile time.
How many Constructors are there in Kotlin?
In Kotlin, there are two types of constructors used to create and initialize objects of a class: Primary Constructor and Secondary Constructor.
1. Primary Constructor:
The primary constructor is part of the class header and provides a concise way to initialize class properties. It does not contain any logic by itself and is typically used for straightforward initialization.
class Student(val firstName: String, var age: Int) {
// class body
}
2. Secondary Constructor:
Secondary constructors are declared inside the class body using the constructor
keyword. These constructors allow you to provide additional initialization logic or different ways to instantiate a class.
class MyCourse {
constructor(id: Int) {
// initialization logic
}
constructor(name: String, id: Int) {
// initialization logic
}
}
While the primary constructor is often sufficient for most needs, secondary constructors are useful when you need multiple ways to instantiate a class with different parameters.
Do primary and secondary constructors have any relationship?
Yes, in Kotlin, there is a relationship between primary and secondary constructors. When a secondary constructor is defined, it must explicitly delegate to the primary constructor, either directly or indirectly. This ensures that the primary constructor's initialization logic is always executed when an object is created.
This is done using the this
keyword within the secondary constructor to call the primary constructor.
class Person(val name: String) {
constructor(name: String, age: Int) : this(name) {
// additional initialization
}
}
In this example, the secondary constructor delegates to the primary constructor using : this(name)
, ensuring that the name
property is initialized correctly.
What is the critical difference between ‘var’ and ‘val’ for Variable declaration in Kotlin?
In Kotlin, the keywords var
and val
are used to declare variables, and they differ primarily in terms of mutability.
var
(mutable): Used when the variable's value can change during the program's execution. Variables declared withvar
are mutable.val
(immutable): Used when the variable's value is read-only and cannot be changed once it is assigned. It behaves like a final variable in Java.
var age = 25
age = 30 // allowed
val name = "Kotlin"
name = "Java" // Error: Val cannot be reassigned
This distinction enforces safer coding practices by encouraging immutability wherever possible, reducing bugs due to unintended value changes.
Is Kotlin functional or OOP?
Kotlin is a modern, versatile programming language that supports both object-oriented programming (OOP) and functional programming paradigms. It allows developers to write clean, maintainable code by combining the best of both worlds.
As an object-oriented language, Kotlin supports classes, objects, inheritance, encapsulation, and polymorphism. You can define data models, encapsulate behavior, and structure applications using traditional OOP techniques.
At the same time, Kotlin also supports functional programming features such as higher-order functions
, lambda expressions
, map/filter/reduce
operations, and immutable data
. These features promote a more declarative and concise coding style that is ideal for handling collections and side-effect-free logic.
This dual capability makes Kotlin highly expressive and suitable for a wide range of programming needs, whether you prefer functional constructs or object-oriented design.
In Kotlin, what is the difference between lateinit and lazy?
In Kotlin, both lateinit
and lazy
are used for delayed initialization, but they differ significantly in usage and behavior. Below is a comparison:
lateinit | lazy |
---|---|
Can only be used with var . | Can only be used with val . |
Can be initialized multiple times. | Initialized only once and then cached. |
Can be initialized from any part of the class. | Must be initialized using the initializer lambda. |
Cannot be used with primitive types. | Can be used with primitive types. |
What is the main difference between FlatMap and Map?
In Kotlin, both map
and flatMap
are functional programming constructs used for transforming collections, but they behave differently in terms of output structure.
map
is used to transform each element of a collection individually using a transformation function. It returns a list where each item is the result of the transformation function applied to the corresponding element in the original list.
flatMap
is used when each transformation produces a collection itself (e.g., a list of items for each element). It flattens the resulting nested lists into a single list.
Example:
// Using map
val mapped = listOf(1, 2, 3).map { listOf(it, -it) }
println(mapped) // Output: [[1, -1], [2, -2], [3, -3]]
// Using flatMap
val flatMapped = listOf(1, 2, 3).flatMap { listOf(it, -it) }
println(flatMapped) // Output: [1, -1, 2, -2, 3, -3]
What is Kotlin Null Safety?
Null Safety in Kotlin is a feature designed to eliminate the risk of null references in your code, commonly known as the 'billion-dollar mistake'. Kotlin enforces strict null checks during compilation, helping developers avoid NullPointerException
at runtime.
In Kotlin, variables are non-nullable by default. If you want a variable to hold a null value, you must explicitly declare it as nullable by appending a ?
to its type. The compiler will then require you to handle the possibility of null values using safe call operators (?.
), the Elvis operator (?:
), or other null-handling constructs.
Example:
var name: String = "Kotlin"
name = null // Compilation error
var nullableName: String? = "Kotlin"
nullableName = null // This is allowed
By enforcing null safety at compile time, Kotlin significantly reduces the chances of runtime null-related crashes, leading to more stable and reliable code.
How do you handle nullability in Kotlin?
In Kotlin, nullability is handled through built-in language features that ensure safe access and manipulation of nullable types. Kotlin distinguishes between nullable and non-nullable variables at the type level, helping developers avoid NullPointerException
errors.
1. Safe Calls (?.
):
The safe call operator is used to safely access a property or call a method on a nullable object. If the object is null
, the entire expression evaluates to null
instead of throwing an exception.
val length: Int? = text?.length
2. Elvis Operator (?:
):
The Elvis operator provides a default value if the expression on the left is null
. This helps ensure a non-null result.
val length: Int = text?.length ?: 0
3. Safe Casts (as?
):
The safe cast operator attempts to cast a value to a specific type. If the cast is not possible, it returns null
instead of throwing an exception.
val name: String? = value as? String
4. Non-Null Assertion Operator (!!
):
If you're certain that a nullable variable is not null
, you can use the non-null assertion operator to force the compiler to treat it as non-null. If the value is actually null
, a NullPointerException
will be thrown.
val length: Int = text!!.length
By using these features, Kotlin ensures a safer and more predictable handling of null values, reducing the likelihood of runtime exceptions.
What is the Elvis operator in Kotlin?
The Elvis operator in Kotlin, represented by ?:
, is a concise and elegant way to assign a default value when dealing with nullable expressions. It is particularly useful in scenarios where a variable might be null, and you want to provide an alternative value to avoid NullPointerException
.
Syntax:
val result = nullableObject ?: defaultValue
If nullableObject
is not null
, the expression evaluates to nullableObject
. Otherwise, it evaluates to defaultValue
.
Example:
val name: String? = null
val length: Int = name?.length ?: 0
In this example, the variable name
is nullable. The safe call operator ?.
attempts to get the length of name
. If name
is null, the Elvis operator ensures that 0
is assigned to the length
variable as a default value.
The Elvis operator simplifies null checks and enhances code readability by reducing the need for verbose if-else
conditions.
What are coroutines in Kotlin?
Coroutines in Kotlin are a concurrency design pattern that allows for efficient and structured asynchronous programming. They provide a way to write asynchronous code that looks like sequential code, making it easier to understand and maintain.
Coroutines can suspend execution without blocking the thread, allowing for non-blocking I/O operations and concurrent computations. They are designed to handle asynchronous tasks in a structured and sequential manner.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(1000L)
println("Coroutine executed")
}
println("Hello")
job.join()
println("World")
}
In the example, we use the launch
function from the kotlinx.coroutines
package to create a coroutine. Inside the coroutine, we use the delay
function to suspend execution for 1000 milliseconds (1 second). The output is printed before and after the coroutine execution. The runBlocking
function is used to start the coroutine and block the main thread until it completes.
Coroutines simplify asynchronous programming by providing a structured and intuitive way to handle concurrency and parallelism.
What's the difference between blocking and suspending?
Blocking a call to a function means that any further calls from the same thread will cause the parent’s execution to halt. If a blocking call is made on the main thread, the UI freezes and the user sees a static screen until the call is completed. This is undesirable in UI-driven applications.
Suspending, on the other hand, does not necessarily stop the parent function from executing. Suspended functions can be moved to a different thread, allowing the main thread to remain responsive. When a suspension point is reached, the execution pauses and the thread is freed for other work, resuming only when necessary. This enables efficient and non-blocking asynchronous programming.
Why Choose Our Question Bank?
Get access to expertly crafted answers and comprehensive preparation materials
Complete Collection
Access all 16 carefully curated questions covering every aspect of Kotlin interviews
Expert Answers
Get detailed, professional answers crafted by industry experts with real-world experience
Instant Access
Start preparing immediately with instant access to all questions and answers after sign-up