Interview

20 Kotlin Interview Questions and Answers

Prepare for your next interview with this guide on Kotlin, covering common questions to help you demonstrate your skills and knowledge effectively.

Kotlin has rapidly gained popularity as a modern programming language, particularly for Android development. Its concise syntax, interoperability with Java, and enhanced safety features make it a preferred choice for developers looking to build robust and maintainable applications. Kotlin’s versatility extends beyond mobile development, finding use in server-side applications, web development, and more.

This article offers a curated selection of Kotlin interview questions designed to help you demonstrate your proficiency and problem-solving abilities. By familiarizing yourself with these questions, you’ll be better prepared to showcase your expertise and stand out in technical interviews.

Kotlin Interview Questions and Answers

1. How do you declare a read-only variable and a mutable variable?

In Kotlin, variables can be declared as either read-only or mutable. Read-only variables use the val keyword, meaning their value cannot be changed once assigned. Mutable variables use the var keyword, allowing their value to be modified.

Example:

val readOnlyVariable: Int = 10
var mutableVariable: Int = 20

// readOnlyVariable = 15 // This will cause a compilation error
mutableVariable = 25 // This is allowed

2. Explain how when expressions work.

The when expression in Kotlin evaluates a variable against multiple conditions and can be used as both a statement and an expression. It handles conditions like ranges, types, and specific values.

Example:

fun describe(obj: Any): String =
    when (obj) {
        1          -> "One"
        "Hello"    -> "Greeting"
        is Long    -> "Long number"
        !is String -> "Not a string"
        else       -> "Unknown"
    }

println(describe(1))        // Output: One
println(describe("Hello"))  // Output: Greeting
println(describe(1000L))    // Output: Long number
println(describe(2.5))      // Output: Not a string
println(describe("Hi"))     // Output: Unknown

3. How do you define a function with default arguments?

You can define a function with default arguments in Kotlin by specifying default values in the parameter list. This allows calling the function without providing all arguments.

Example:

fun greet(name: String, greeting: String = "Hello") {
    println("$greeting, $name!")
}

greet("Alice") // Output: Hello, Alice!
greet("Bob", "Hi") // Output: Hi, Bob!

4. What is the purpose of the ? operator?

The ? operator in Kotlin denotes nullable types and safely accesses properties and methods of nullable objects, preventing null pointer exceptions.

Example:

val name: String? = null
println(name?.length) // Output: null

The Elvis operator (?:) provides a default value if the expression on the left is null:

val name: String? = null
val length = name?.length ?: 0
println(length) // Output: 0

5. What are extension functions and how do you use them?

Extension functions in Kotlin allow you to add functionality to a class without modifying its source code. This is done by defining a function outside the class with the class as its receiver type.

Example:

fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

fun main() {
    val word = "radar"
    println(word.isPalindrome())  // Output: true
}

6. How do you pass a function as a parameter to another function?

Functions in Kotlin are first-class citizens, meaning they can be stored in variables, passed as arguments, and returned from other functions. Higher-order functions take other functions as parameters or return them.

Example:

fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

fun add(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    val result = calculate(5, 3, ::add)
    println(result) // Output: 8
}

7. Explain how coroutines work.

Coroutines in Kotlin simplify asynchronous programming by allowing code to be sequential and non-blocking. They are built on suspending functions, which can be paused and resumed.

Example:

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

8. How do you define and use sealed classes?

Sealed classes in Kotlin represent a restricted hierarchy of classes, allowing you to define a closed set of subclasses known at compile time.

Example:

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val exception: Exception) : Result()
    object Loading : Result()
}

fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println("Data: ${result.data}")
        is Result.Error -> println("Error: ${result.exception.message}")
        Result.Loading -> println("Loading...")
    }
}

9. What are data classes and what benefits do they offer?

Data classes in Kotlin are used to hold data and automatically generate methods like equals(), hashCode(), toString(), and copy(). They support destructuring declarations and encourage immutability.

Example:

data class User(val name: String, val age: Int)

fun main() {
    val user1 = User("Alice", 30)
    val user2 = user1.copy(age = 31)

    println(user1) // Output: User(name=Alice, age=30)
    println(user2) // Output: User(name=Alice, age=31)

    val (name, age) = user1
    println("Name: $name, Age: $age") // Output: Name: Alice, Age: 30
}

10. How do you define a generic function or class?

Generics in Kotlin allow you to define classes and functions that can operate on any type, providing reusable and type-safe code.

Example of a generic function:

fun <T> printList(items: List<T>) {
    for (item in items) {
        println(item)
    }
}

Example of a generic class:

class Box<T>(val value: T) {
    fun getValue(): T {
        return value
    }
}

11. What is delegation and how is it implemented?

Delegation in Kotlin allows an object to delegate responsibilities to another object using the by keyword, promoting code reuse and composition.

Example:

interface Printer {
    fun print()
}

class SimplePrinter : Printer {
    override fun print() {
        println("Printing from SimplePrinter")
    }
}

class AdvancedPrinter(printer: Printer) : Printer by printer

fun main() {
    val simplePrinter = SimplePrinter()
    val advancedPrinter = AdvancedPrinter(simplePrinter)
    advancedPrinter.print()  // Output: Printing from SimplePrinter
}

12. How do you create and use annotations?

Annotations in Kotlin are created using the annotation keyword and can be used to annotate classes, methods, or properties. They can have parameters for additional information.

Example:

annotation class MyAnnotation(val info: String)

@MyAnnotation(info = "This is a sample annotation")
class AnnotatedClass {
    @MyAnnotation(info = "This is a sample method annotation")
    fun annotatedMethod() {
        println("This method is annotated")
    }
}

fun main() {
    val annotations = AnnotatedClass::class.annotations
    for (annotation in annotations) {
        if (annotation is MyAnnotation) {
            println("Class annotation info: ${annotation.info}")
        }
    }

    val methodAnnotations = AnnotatedClass::class.members.find { it.name == "annotatedMethod" }?.annotations
    if (methodAnnotations != null) {
        for (annotation in methodAnnotations) {
            if (annotation is MyAnnotation) {
                println("Method annotation info: ${annotation.info}")
            }
        }
    }
}

13. Explain the concept of null safety.

In Kotlin, types are non-nullable by default. You can assign a null value to a variable by explicitly declaring it as nullable using the ? symbol.

Example:

var nonNullableString: String = "Hello"
// nonNullableString = null // This will cause a compilation error

var nullableString: String? = "Hello"
nullableString = null // This is allowed

Kotlin provides operators to handle nullable types safely:

– Safe call operator (?.)
– Elvis operator (?:)
– Not-null assertion operator (!!)

Example:

val length: Int? = nullableString?.length // Safe call operator
val lengthOrDefault: Int = nullableString?.length ?: 0 // Elvis operator
val nonNullLength: Int = nullableString!!.length // Not-null assertion operator

14. What are higher-order functions and how are they used?

Higher-order functions in Kotlin take other functions as parameters or return them, allowing for flexible and reusable code.

Example:

fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
    val result = mutableListOf<T>()
    for (item in this) {
        if (predicate(item)) {
            result.add(item)
        }
    }
    return result
}

val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.customFilter { it % 2 == 0 }
println(evenNumbers) // Output: [2, 4]

15. How do you define and use companion objects?

Companion objects in Kotlin define static members of a class, accessible using the class name without needing an instance.

Example:

class MyClass {
    companion object {
        const val CONSTANT_VALUE = "Hello, World!"

        fun printMessage() {
            println(CONSTANT_VALUE)
        }
    }
}

fun main() {
    println(MyClass.CONSTANT_VALUE)
    MyClass.printMessage()
}

16. What are smart casts and how do they simplify type checking?

Smart casts in Kotlin simplify type checking and casting. When you use the is operator, the compiler automatically casts the variable to the target type within the valid scope.

Example:

fun printLength(obj: Any) {
    if (obj is String) {
        println(obj.length)
    } else {
        println("Not a string")
    }
}

17. How do destructuring declarations work?

Destructuring declarations in Kotlin allow you to unpack multiple values from an object into individual variables, improving code readability.

Example:

data class User(val name: String, val age: Int)

val user = User("Alice", 30)
val (name, age) = user

println(name) // Alice
println(age)  // 30

Destructuring can also be applied to collections:

val list = listOf(1, 2, 3)
val (a, b, c) = list

println(a) // 1
println(b) // 2
println(c) // 3

18. What is Kotlin Multiplatform and how is it used?

Kotlin Multiplatform enables developers to write code that can be shared across multiple platforms, reducing duplicated code and effort.

Example:

// Common code
expect fun getPlatformName(): String

// Android-specific code
actual fun getPlatformName(): String {
    return "Android"
}

// iOS-specific code
actual fun getPlatformName(): String {
    return "iOS"
}

19. What is the purpose of the lateinit keyword?

The lateinit keyword in Kotlin declares a non-null property that will be initialized later. It can only be used with var properties.

Example:

class Example {
    lateinit var name: String

    fun initializeName() {
        name = "Kotlin"
    }

    fun printName() {
        if (::name.isInitialized) {
            println(name)
        } else {
            println("Name is not initialized")
        }
    }
}

fun main() {
    val example = Example()
    example.printName() // Output: Name is not initialized
    example.initializeName()
    example.printName() // Output: Kotlin
}

20. What are inline functions and when would you use them?

Inline functions in Kotlin optimize higher-order functions by reducing the overhead of function calls. The compiler replaces the function call with the actual code of the function.

Example:

inline fun <T> measureTime(block: () -> T): T {
    val start = System.currentTimeMillis()
    val result = block()
    val end = System.currentTimeMillis()
    println("Time taken: ${end - start} ms")
    return result
}

fun main() {
    val result = measureTime {
        Thread.sleep(1000)
        "Task Completed"
    }
    println(result)
}
Previous

15 Xamarin Interview Questions and Answers

Back to Interview
Next

15 Database Testing Interview Questions and Answers