Kotlin Interview Questions and Answers


What is Kotlin and why is it popular?
  • Kotlin is a statically-typed, general-purpose programming language developed by JetBrains. It is popular because it is concise, safe, interoperable with Java, and has excellent tooling support, especially for Android development.
What are the key features of Kotlin?
  • Conciseness (less boilerplate code)
  • Null safety (reduces NullPointerException errors)
  • Interoperability with Java (seamless integration with existing Java code)
  • Extension functions (add new functionality to existing classes)
  • Data classes (automatically generates boilerplate code for data holders)
  • Sealed classes (represent restricted class hierarchies)
  • Coroutines (for asynchronous programming)
  • Scope functions (apply, also, let, run, with)
  • Smart casts
  • Operator overloading
  • Delegation
Is Kotlin 100% interoperable with Java?
  • Yes, Kotlin is designed to be 100% interoperable with Java. You can call Kotlin code from Java and Java code from Kotlin seamlessly.
What are the advantages of using Kotlin over Java?
  • More concise code
  • Improved null safety
  • Better expressiveness
  • Modern language features
  • Reduced boilerplate
  • Excellent tooling
  • Growing community and adoption
What are the disadvantages of Kotlin?
  • Smaller community compared to Java (though growing rapidly)
  • Compilation speed can sometimes be slightly slower than Java for very large projects (improving with newer versions)
  • Finding experienced Kotlin developers might be slightly harder than finding experienced Java developers (changing rapidly)
How does Kotlin handle null safety?
  • Kotlin distinguishes between nullable and non-nullable types at compile time. By default, types are non-nullable. To make a type nullable, you append a `?` to the type name (e.g., `String?`). The compiler enforces checks to prevent accessing nullable types without handling the null case.
Explain the difference between `var` and `val` in Kotlin.
  • `var` (mutable variable): The value assigned to a `var` can be reassigned.
    var name = "Alice"
    name = "Bob" // Allowed
  • `val` (read-only variable): The value assigned to a `val` can only be assigned once. It's like a `final` variable in Java.
    val age = 30
    // age = 31 // Not allowed, compilation error
What is a data class in Kotlin? Give an example.
  • A data class is a concise way to create classes that primarily hold data. The compiler automatically generates boilerplate code like `equals()`, `hashCode()`, `toString()`, `copy()`, and `componentN()` functions for the properties declared in the primary constructor.
    data class User(val name: String, val age: Int)
    
    fun main() {
        val user1 = User("Alice", 30)
        val user2 = User("Alice", 30)
    
        println(user1 == user2) // true (due to generated equals())
        println(user1.toString()) // User(name=Alice, age=30) (due to generated toString())
    
        val user3 = user1.copy(age = 31) // due to generated copy()
        println(user3) // User(name=Alice, age=31)
    }
What are extension functions in Kotlin? Give an example.
  • Extension functions allow you to add new functions to existing classes without modifying their source code. This is useful for adding utility functions or making APIs more readable.
    fun String.addExclamationMark(): String {
        return this + "!"
    }
    
    fun main() {
        val greeting = "Hello"
        println(greeting.addExclamationMark()) // Hello!
    }
Explain smart casts in Kotlin.
  • Kotlin's smart casts allow you to treat a variable as a specific type after a type check (`is` or `!is`) without explicit casting. The compiler automatically casts the variable within the scope of the check.
    fun printLength(obj: Any) {
        if (obj is String) {
            // obj is automatically cast to String here
            println("String length is ${obj.length}")
        } else if (obj !is String) {
            println("Not a String")
        }
    }
What is the difference between `is` and `as` in Kotlin?
  • `is`: Used for type checking. It checks if an object is an instance of a specific type. Often used with smart casts.
    if (obj is String) { ... }
  • `as`: Used for explicit type casting. The `as?` safe cast operator returns `null` if the cast is not possible, preventing a `ClassCastException`.
    val str = obj as String // Throws ClassCastException if obj is not a String
    val safeStr = obj as? String // Returns null if obj is not a String
What are sealed classes in Kotlin?
  • Sealed classes are used to represent restricted class hierarchies. All direct subclasses of a sealed class must be declared within the same file as the sealed class itself. This allows the compiler to know all possible subclasses at compile time, enabling exhaustive `when` expressions.
    sealed class Result {
        data class Success(val data: String) : Result()
        data class Error(val message: String) : Result()
    }
    
    fun processResult(result: Result) {
        when (result) {
            is Result.Success -> println("Success: ${result.data}")
            is Result.Error -> println("Error: ${result.message}")
            // The compiler knows all possible cases because Result is sealed
        }
    }
Explain the concept of coroutines in Kotlin.
  • Coroutines are a lightweight way to handle asynchronous programming in Kotlin. They allow you to write asynchronous code in a sequential, blocking-like style, making it easier to understand and maintain compared to traditional callbacks or futures. Coroutines are not tied to specific threads and can suspend and resume execution.
What is the difference between a coroutine and a thread?
  • **Threads:** Managed by the operating system. Creating many threads can be resource-intensive and lead to context switching overhead. Threads are blocking.
  • **Coroutines:** Managed by the Kotlin runtime. They are much lighter than threads and can run on a single thread. Coroutines are non-blocking and can suspend and resume.
What are the main building blocks of coroutines in Kotlin?
  • `suspend` functions: Functions that can be paused and resumed.
  • `CoroutineScope`: Defines the lifecycle of coroutines.
  • `Job`: Represents a cancellable unit of work.
  • `CoroutineContext`: A set of elements that define the behavior of a coroutine (e.g., Dispatcher, Job).
  • `Dispatchers`: Determines which thread(s) a coroutine runs on (e.g., `Dispatchers.Default`, `Dispatchers.IO`, `Dispatchers.Main`).
What are scope functions (`apply`, `also`, `let`, `run`, `with`)? Explain their differences.
  • Scope functions allow you to execute a block of code on an object within a certain context, making the code more concise and readable.
    • `apply`: Executes a block of code on an object and returns the object itself (`this`). Useful for configuring an object.
    • `also`: Executes a block of code on an object and returns the object itself (`it`). Useful for performing additional actions on an object after initialization (e.g., logging).
    • `let`: Executes a block of code on an object and returns the result of the lambda (`it`). Useful for null checks or transforming an object.
    • `run`: Can be used as an extension function (`this`) or a non-extension function. As an extension function, it executes a block of code on an object and returns the result of the lambda. Useful for configuring an object and computing a result. As a non-extension function, it simply executes a block of code and returns the result.
    • `with`: A non-extension function (`this`) that executes a block of code on an object and returns the result of the lambda. Useful for performing multiple operations on an object without repeatedly mentioning its name.
What is the Elvis operator (`?:`) in Kotlin?
  • The Elvis operator (`?:`) is used for handling nullable types. It provides a concise way to return a non-null value if the expression on the left is not null, otherwise it returns the expression on the right (which must be non-null).
    val name: String? = null
    val displayName = name ?: "Guest" // displayName will be "Guest"
What is the safe call operator (`?.`) in Kotlin?
  • The safe call operator (`?.`) is used for calling methods or accessing properties on nullable objects. If the object is not null, the method or property is called; otherwise, the expression evaluates to `null`.
    val name: String? = null
    val length = name?.length // length will be null
What is the not-null assertion operator (`!!`) in Kotlin?
  • The not-null assertion operator (`!!`) converts a nullable type to a non-nullable type. If the nullable value is `null` at runtime, it throws a `NullPointerException`. Use this operator with caution, only when you are absolutely certain that the value will not be null.
    val name: String? = "Alice"
    val length = name!!.length // Throws NullPointerException if name is null
How do you define a function in Kotlin?
  • You define a function using the `fun` keyword.
    fun add(a: Int, b: Int): Int {
        return a + b
    }
What is a single-expression function?
  • If a function's body consists of a single expression, you can use the `=` operator to define it more concisely. The return type can often be inferred.
    fun add(a: Int, b: Int): Int = a + b
What are default arguments in Kotlin?
  • Kotlin allows you to define default values for function parameters. This makes it possible to call the function with fewer arguments.
    fun greet(name: String = "Guest") {
        println("Hello, $name")
    }
    
    fun main() {
        greet() // Hello, Guest
        greet("Alice") // Hello, Alice
    }
What are named arguments in Kotlin?
  • When calling a function, you can specify the names of the arguments. This improves readability, especially for functions with many parameters or default arguments.
    fun configure(width: Int = 100, height: Int = 200) {
        println("Width: $width, Height: $height")
    }
    
    fun main() {
        configure(height = 300) // Width: 100, Height: 300
    }
Explain the concept of primary and secondary constructors in Kotlin.
  • **Primary constructor:** Declared in the class header. It's the main way to initialize a class. Properties can be declared directly in the primary constructor.
    class Person(val name: String, var age: Int)
  • **Secondary constructors:** Declared using the `constructor` keyword within the class body. A secondary constructor must delegate to the primary constructor (directly or indirectly) using the `this` keyword.
    class Person {
        val name: String
        var age: Int
    
        constructor(name: String, age: Int) {
            this.name = name
            this.age = age
        }
    
        constructor(name: String) : this(name, 0) // Delegates to the primary constructor
    }
What is the `init` block in Kotlin?
  • The `init` block is part of the class initialization process. Code inside the `init` block is executed when an instance of the class is created, after the primary constructor (if any) has been processed. A class can have multiple `init` blocks, and they are executed in the order they appear in the class body.
    class Person(name: String) {
        val greeting = "Hello, $name!"
    
        init {
            println("Initializing a Person with name: $name")
        }
    
        init {
            println("Another init block")
        }
    }
How does inheritance work in Kotlin?
  • Classes are `final` by default in Kotlin. To allow a class to be inherited from, you need to mark it with the `open` keyword. To override a method or property in a subclass, you need to mark the overridden member with the `override` keyword and the member in the superclass with the `open` keyword (if it's not already open).
    open class Animal {
        open fun makeSound() {
            println("Generic animal sound")
        }
    }
    
    class Dog : Animal() {
        override fun makeSound() {
            println("Woof!")
        }
    }
What is the difference between an abstract class and an interface in Kotlin?
  • **Abstract class:** Can have abstract and non-abstract members. Can have constructors. Can maintain state (have properties with backing fields). A class can inherit from only one abstract class.
  • **Interface:** Can have abstract and non-abstract members (with default implementations). Cannot have constructors. Cannot maintain state (properties in interfaces are abstract or provide accessor implementations, they don't have backing fields). A class can implement multiple interfaces.
What is the `object` keyword used for in Kotlin?
  • The `object` keyword can be used to declare:
    • **Object declarations:** A singleton instance of a class.
    • **Companion objects:** A single instance associated with a class, often used for factory methods or static-like members.
    • **Object expressions:** Anonymous objects, similar to anonymous inner classes in Java.
Explain object declarations (singletons) in Kotlin.
  • An object declaration creates a singleton instance of a class. You define it using the `object` keyword. The instance is created lazily when it's first accessed.
    object DatabaseManager {
        fun connect() {
            println("Connecting to database...")
        }
    }
    
    fun main() {
        DatabaseManager.connect() // Accessing the singleton instance
    }
Explain companion objects in Kotlin.
  • A companion object is a special object declaration within a class. It's associated with the class itself, not with instances of the class. Members of a companion object can be accessed directly using the class name, similar to static members in Java.
    class MyClass {
        companion object {
            const val TAG = "MyClass" // Constants
    
            fun create(): MyClass { // Factory method
                return MyClass()
            }
        }
    }
    
    fun main() {
        println(MyClass.TAG)
        val instance = MyClass.create()
    }
What are object expressions (anonymous objects)?
  • Object expressions are used to create anonymous objects that can implement interfaces or inherit from classes. They are similar to anonymous inner classes in Java.
    interface ClickListener {
        fun onClick()
    }
    
    fun setClickListener(listener: ClickListener) {
        // ...
    }
    
    fun main() {
        setClickListener(object : ClickListener {
            override fun onClick() {
                println("Button clicked!")
            }
        })
    }
What is delegation in Kotlin? Give an example.
  • Delegation is a design pattern where an object forwards a request to another object. Kotlin supports class delegation natively using the `by` keyword. This is useful for implementing the Decorator pattern or mixing in behavior from interfaces.
    interface Printer {
        fun print()
    }
    
    class RealPrinter : Printer {
        override fun print() {
            println("Printing...")
        }
    }
    
    class DelegatingPrinter(private val printer: Printer) : Printer by printer
    
    fun main() {
        val realPrinter = RealPrinter()
        val delegatingPrinter = DelegatingPrinter(realPrinter)
        delegatingPrinter.print() // Delegates the call to RealPrinter
    }
Explain delegated properties in Kotlin.
  • Delegated properties allow you to delegate the getter and setter logic of a property to another object. Kotlin provides standard delegates like `lazy`, `observable`, and `vetoable`, and you can create custom ones.
    import kotlin.properties.Delegates
    
    class Example {
        var name: String by Delegates.observable("") {
            prop, old, new ->
            println("$old -> $new")
        }
    }
    
    fun main() {
        val e = Example()
        e.name = "First" //  -> First
        e.name = "Second" // First -> Second
    }
What is the `lazy` delegate?
  • The `lazy` delegate is used for lazy initialization of a `val` property. The value is computed only on the first access and then stored for subsequent accesses.
    val lazyValue: String by lazy {
        println("Computing the value...")
        "Hello"
    }
    
    fun main() {
        println(lazyValue) // "Computing the value..." is printed, then "Hello"
        println(lazyValue) // "Hello" (value is already computed)
    }
What are collections in Kotlin? Name some common collection types.
  • Kotlin provides a rich set of collection types, both mutable and immutable. Common types include:
    • Lists (`List`, `MutableList`)
    • Sets (`Set`, `MutableSet`)
    • Maps (`Map`, `MutableMap`)
Explain the difference between immutable and mutable collections.
  • **Immutable collections:** Cannot be modified after they are created. Operations like adding or removing elements return a new collection. (`List`, `Set`, `Map`)
  • **Mutable collections:** Can be modified after they are created. Operations like adding or removing elements modify the original collection. (`MutableList`, `MutableSet`, `MutableMap`)
How do you create an immutable list in Kotlin?
  • You can use the `listOf()` function.
    val numbers = listOf(1, 2, 3)
How do you create a mutable list in Kotlin?
  • You can use the `mutableListOf()` function.
    val numbers = mutableListOf(1, 2, 3)
    numbers.add(4) // Allowed
What are higher-order functions in Kotlin?
  • A higher-order function is a function that takes another function as a parameter or returns a function.
    fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
        return operation(a, b)
    }
    
    fun main() {
        val sum = operate(5, 3) { x, y -> x + y } // Passing a lambda as an argument
        println(sum) // 8
    }
What is a lambda expression (or anonymous function) in Kotlin?
  • A lambda expression is a concise way to define a function literal (a function that is not declared, but passed immediately as an expression).
    { a: Int, b: Int -> a + b }
What is the `it` keyword in Kotlin?
  • In a lambda expression with a single parameter, you can implicitly refer to that parameter using the `it` keyword instead of explicitly naming it.
    val numbers = listOf(1, 2, 3)
    val doubled = numbers.map { it * 2 } // 'it' refers to each element in the list
What is the difference between `fun` and `val` for defining functions?
  • `fun`: Used to declare a regular function.
  • `val`: Can be used to store a lambda expression or a function reference in a variable.
    fun add(a: Int, b: Int): Int = a + b
    
    val subtract: (Int, Int) -> Int = { a, b -> a - b }
Explain the concept of generics in Kotlin.
  • Generics allow you to write code that works with different types while maintaining type safety. They are used to create classes, interfaces, and functions that can operate on types specified as parameters.
    class Box<T>(val value: T)
    
    fun <T> printBox(box: Box<T>) {
        println(box.value)
    }
What is the difference between declaration-site variance and use-site variance?
  • **Declaration-site variance:** Variance is specified when the generic class or interface is declared using `in` or `out`. This applies the variance to all usages of that generic type.
  • **Use-site variance:** Variance is specified when the generic type is used (e.g., as a function parameter) using type projections (`out` or `in`). This applies the variance only to that specific usage. Kotlin supports both, but declaration-site variance is preferred when possible.
What are inline functions in Kotlin?
  • An inline function is a function that is "inlined" by the compiler at the call site. Instead of generating a function call, the compiler copies the body of the inline function into the code where it is called. This can improve performance by avoiding the overhead of function calls, especially for higher-order functions with lambda parameters.
    inline fun measureTime(block: () -> Unit) {
        val start = System.currentTimeMillis()
        block()
        val end = System.currentTimeMillis()
        println("Execution time: ${end - start} ms")
    }
    
    fun main() {
        measureTime {
            // Some code to measure
        }
    }
When should you use inline functions?
  • Use inline functions primarily for higher-order functions that take lambda parameters to avoid the overhead of creating function objects for the lambdas. Avoid inlining large functions, as it can increase code size.
What is a `crossinline` modifier?
  • The `crossinline` modifier is used with inline function parameters (lambdas). It prevents the lambda from performing a non-local return (returning from the enclosing function). This is useful when the lambda is called from another execution context (like a separate thread or a different function).
What is a `noinline` modifier?
  • The `noinline` modifier is used with inline function parameters (lambdas) to prevent a specific lambda from being inlined. This is useful when you need to pass the lambda as a function object to another function or store it in a variable.
What is the difference between `return` and `return@label` in Kotlin?
  • `return`: By default, a `return` statement in a lambda expression performs a non-local return, meaning it returns from the enclosing function where the lambda was called.
  • `return@label`: Used for local returns. It returns from the labeled function or lambda.
    fun foo() {
        listOf(1, 2, 3).forEach {
            if (it == 2) return // Non-local return from foo()
            println(it)
        }
        println("This will not be printed if 2 is in the list")
    }
    
    fun bar() {
        listOf(1, 2, 3).forEach lit@{
            if (it == 2) return@lit // Local return from the lambda
            println(it)
        }
        println("Done with bar()") // This will be printed
    }
What is the `const` keyword in Kotlin?
  • The `const` keyword is used to declare compile-time constants. They must be top-level or members of an `object` or a `companion object`. Their value must be known at compile time (primitive types or `String`). They are inlined at compile time.
    const val PI = 3.14159
What is the difference between `val` and `const val`?
  • `val`: Read-only property. Its value is assigned at runtime (though it can be initialized only once). Can be any type. Not necessarily known at compile time.
  • `const val`: Compile-time constant. Its value must be known at compile time. Must be a primitive type or `String`. Inlined at compile time.
What are annotations in Kotlin?
  • Annotations are a way to attach metadata to code elements (classes, functions, properties, etc.). They don't directly affect the execution of the code but can be used by tools, libraries, or the compiler. Kotlin has built-in annotations and allows you to define custom ones.
    @Deprecated("Use newFunction instead")
    fun oldFunction() { ... }
What is the purpose of the `@JvmStatic` annotation?
  • The `@JvmStatic` annotation is used in companion objects or object declarations to expose a function or property as a static member in the generated Java bytecode. This makes it easier to call from Java code.
    class MyClass {
        companion object {
            @JvmStatic
            fun staticMethod() { ... }
        }
    }
What is the purpose of the `@JvmOverloads` annotation?
  • The `@JvmOverloads` annotation is used on functions with default arguments to generate overloaded methods in the Java bytecode for each combination of default arguments. This makes it easier to call the function from Java code with fewer arguments.
    class MyClass {
        @JvmOverloads
        fun foo(a: String, b: Int = 0, c: Double = 0.0) { ... }
    }
    // In Java:
    // new MyClass().foo("hello");
    // new MyClass().foo("hello", 1);
    // new MyClass().foo("hello", 1, 2.0);
What is the purpose of the `@JvmField` annotation?
  • The `@JvmField` annotation is used on properties to expose them as public fields in the generated Java bytecode instead of generating getter/setter methods. This is useful for performance or when interoperating with Java code that expects public fields.
    class MyClass( @JvmField val myField: String)
What is the difference between a `class` and a `data class`?
  • A regular `class` is a general-purpose blueprint for objects. You need to manually define methods like `equals()`, `hashCode()`, `toString()`, etc., if you need them.
  • A `data class` is specifically for holding data. The compiler automatically generates the boilerplate methods (`equals()`, `hashCode()`, `toString()`, `copy()`, `componentN()`) based on the properties declared in the primary constructor.
What are enums in Kotlin?
  • Enums (enumerated classes) are used to define a set of named constants. Similar to Java enums, they can have properties and methods.
    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }
    
    fun main() {
        println(Color.RED.rgb) // 16711680
    }
What is the difference between `enum class` and `sealed class`?
  • `enum class`: Represents a fixed set of named constants. All instances are predefined. Cannot be subclassed externally.
  • `sealed class`: Represents a restricted hierarchy of classes. Subclasses are known at compile time but can be instances of different classes. Can have state and behavior defined in subclasses.
What is destructuring declaration in Kotlin?
  • Destructuring declaration allows you to unpack an object into a set of variables. This is commonly used with data classes and pairs/triples.
    data class Point(val x: Int, val y: Int)
    
    fun main() {
        val (x, y) = Point(10, 20)
        println("x = $x, y = $y") // x = 10, y = 20
    }
How do you handle exceptions in Kotlin?
  • Kotlin uses the standard `try`, `catch`, and `finally` blocks for exception handling, similar to Java. However, Kotlin's exceptions are unchecked by default (you don't need to declare which exceptions a function might throw).
    fun readFile(path: String) {
        try {
            // Read file
        } catch (e: IOException) {
            println("Error reading file: ${e.message}")
        } finally {
            // Close resources
        }
    }
What is the `throw` keyword used for?
  • The `throw` keyword is used to explicitly throw an exception.
    fun divide(a: Int, b: Int): Int {
        if (b == 0) {
            throw IllegalArgumentException("Cannot divide by zero")
        }
        return a / b
    }
What is a range in Kotlin? How do you create one?
  • A range represents a sequence of values. You can create ranges using the `..` operator for inclusive ranges and `until` for exclusive ranges.
    val range1 = 1..5 // 1, 2, 3, 4, 5
    val range2 = 1 until 5 // 1, 2, 3, 4
How do you iterate over a range?
  • You can iterate over a range using a `for` loop.
    for (i in 1..5) {
        println(i)
    }
What is the `when` expression in Kotlin?
  • The `when` expression is a more flexible replacement for the `switch` statement in Java. It can be used with various types and supports checking ranges and types.
    fun describe(obj: Any): String =
        when (obj) {
            1 -> "One"
            "Hello" -> "Greeting"
            is Long -> "Long"
            !is String -> "Not a string"
            else -> "Unknown"
        }
Can `when` be used as a statement?
  • Yes, `when` can be used as a statement if the result is not used. In this case, the branches can contain blocks of code.
What is the difference between `==` and `===` in Kotlin?
  • `==` (structural equality): Compares the values of two objects. For primitive types, it compares their values. For objects, it calls the `equals()` method.
  • `===` (referential equality): Checks if two references point to the same object in memory.
What is the `Unit` type in Kotlin?
  • `Unit` is a type that corresponds to the `void` return type in Java. A function that doesn't explicitly return a value will implicitly return `Unit`. It's a singleton object.
    fun printMessage(message: String): Unit { // Explicitly returning Unit
        println(message)
    }
    
    fun printMessageImplicit(message: String) { // Implicitly returns Unit
        println(message)
    }
What is the `Nothing` type in Kotlin?
  • `Nothing` is a special type that represents "a value that never exists". It's used to indicate that a function never returns normally (e.g., it always throws an exception or enters an infinite loop). `Nothing` is a subtype of all other types.
    fun fail(message: String): Nothing {
        throw IllegalArgumentException(message)
    }
What is the difference between `lateinit` and `lazy`?
  • `lateinit`: Used with mutable properties (`var`). It allows you to declare a non-nullable property without initializing it immediately. You promise to initialize it later before the first access. If you access it before initialization, it throws a `UninitializedPropertyAccessException`. Cannot be used with primitive types or nullable types.
  • `lazy`: Used with read-only properties (`val`). It delegates the initialization to a lambda, and the value is computed only on the first access. The result is stored and returned on subsequent accesses. Suitable for expensive computations or when the value is not needed immediately.
What are infix functions? How do you define one?
  • Infix functions are member functions or extension functions that can be called using infix notation (without the dot and parentheses), making the code more readable. They must have a single parameter and be marked with the `infix` keyword.
    infix fun Int.times(str: String) = str.repeat(this)
    
    fun main() {
        println(2 times "Bye ") // Bye Bye
    }
What is type aliasing in Kotlin?
  • Type aliasing allows you to provide an alternative name for an existing type. This can improve code readability, especially for complex type signatures or when working with functional types. It doesn't create a new type, just a new name for an existing one.
    typealias Node = Map<String, Any>
    
    fun processNode(node: Node) { ... }
How do you declare a variable that can hold a function?
  • You can declare a variable that holds a function using the function type notation `(ParameterTypes) -> ReturnType`.
    val sum: (Int, Int) -> Int = { a, b -> a + b }
    val operation: (Int, Int, (Int, Int) -> Int) -> Int = { x, y, op -> op(x, y) }
What is a receiver object in extension functions and lambdas with receivers?
  • In an extension function or a lambda with a receiver, the receiver object is the instance on which the function or lambda is invoked. Inside the function/lambda body, you can access the members of the receiver object directly using `this`.
    fun String.print() { // String is the receiver type, 'this' refers to the String instance
        println(this)
    }
    
    val printString: String.() -> Unit = { // String is the receiver type, 'this' refers to the String instance
        println(this)
    }
Explain the difference between `let` and `apply` scope functions.
  • `let`: The receiver object is available as `it`. It returns the result of the lambda. Often used for null checks or transforming the object.
  • `apply`: The receiver object is available as `this`. It returns the receiver object itself. Often used for configuring the object.
Explain the difference between `run` and `with` scope functions.
  • `run`: Can be an extension function or a non-extension function. As an extension function, the receiver is `this`, and it returns the result of the lambda. As a non-extension, it simply executes a block of code and returns the result.
  • `with`: A non-extension function. The object is passed as an argument, and inside the lambda, you access its members using `this`. It returns the result of the lambda.
Explain the difference between `also` and `let` scope functions.
  • `also`: The receiver object is available as `it`. It returns the receiver object itself. Useful for performing side effects (like logging) after some operations.
  • `let`: The receiver object is available as `it`. It returns the result of the lambda. Useful for null checks or transforming the object.
What is the `operator` keyword used for?
  • The `operator` keyword is used to mark a function as an operator function, allowing it to be called using operator notation (e.g., `a + b` instead of `a.plus(b)`). Kotlin defines a set of functions that can be overloaded as operators (e.g., `plus`, `minus`, `times`, `div`, `rem`, `contains`, `get`, `set`, `invoke`, etc.).
How do you overload operators in Kotlin? Give an example.
  • You define a member function or extension function with the `operator` keyword and the corresponding predefined name for the operator.
    data class Point(val x: Int, val y: Int) {
        operator fun plus(other: Point): Point {
            return Point(x + other.x, y + other.y)
        }
    }
    
    fun main() {
        val p1 = Point(1, 2)
        val p2 = Point(3, 4)
        val p3 = p1 + p2 // Calls p1.plus(p2)
        println(p3) // Point(x=4, y=6)
    }
What is the `invoke` operator? When is it used?
  • The `invoke` operator allows you to call an object as if it were a function. You define an `operator fun invoke(...)` in a class, and then instances of that class can be called using parentheses. This is often used for creating callable objects or implementing function-like behavior in classes.
    class Greeter {
        operator fun invoke(name: String) {
            println("Hello, $name")
        }
    }
    
    fun main() {
        val greeter = Greeter()
        greeter("Alice") // Calls greeter.invoke("Alice")
    }
What is a property with a backing field?
  • In Kotlin, properties automatically generate a backing field and accessor methods (getter and setter) by default. The backing field stores the value of the property. You can access the backing field explicitly within the custom getter/setter using the `field` identifier.
    class Person {
        var name: String = ""
            get() = field.toUpperCase() // Custom getter using the backing field
            set(value) {
                field = value.trim() // Custom setter using the backing field
            }
    }
When would you need to use a backing field explicitly?
  • You need to use the backing field (`field`) explicitly when you define custom getters or setters for a property and need to access or modify the actual stored value of the property to avoid recursive calls to the accessor methods.
What is the difference between a nullable receiver and a non-nullable receiver in extension functions?
  • **Non-nullable receiver:** An extension function defined on a non-nullable type can only be called on non-null instances of that type. Inside the function, `this` is guaranteed to be non-null.
    fun String.isShort() = this.length < 5
  • **Nullable receiver:** An extension function defined on a nullable type (`Type?`) can be called on both null and non-null instances. Inside the function, `this` is nullable, and you need to handle the null case (e.g., using `?.` or `if (this != null)`).
    fun String?.isNullOrEmpty(): Boolean {
        return this == null || this.isEmpty()
    }
What are type projections? Give an example.
  • Type projections are a form of use-site variance. They allow you to specify variance constraints on a generic type argument when it's used in a specific context (like a function parameter). The `out` and `in` keywords are used for type projections.
    fun copy(from: Array<out Any>, to: Array<Any>) { // 'out Any' means you can read Any or its subtypes
        for (i in from.indices) {
            to[i] = from[i]
        }
    }
What is the star-projection (`*`)?
  • The star-projection (`*`) is used when you don't know the specific type argument but still want to use the generic type in a safe way.
    • For `Foo`, `Foo<*>` is equivalent to `Foo`. You can read values of type `Any?`.
    • For `Foo`, `Foo<*>` is equivalent to `Foo`. You can't write anything useful (except `null` if nullable).
    • For `Foo`, `Foo<*>` is equivalent to `Foo` when reading and `Foo` when writing.
What is the difference between `Iterable`, `Collection`, `List`, `Set`, and `Map`?
  • `Iterable`: The base interface for collections that can be iterated over. Provides the `iterator()` method.
  • `Collection`: Extends `Iterable`. Represents a group of elements. Provides methods like `size`, `contains`, `isEmpty`, etc.
  • `List`: Extends `Collection`. Represents an ordered collection of elements (can contain duplicates). Elements are accessed by index.
  • `Set`: Extends `Collection`. Represents an unordered collection of unique elements.
  • `Map`: Represents a collection of key-value pairs. Keys are unique. Not a subtype of `Collection`.
What is the purpose of the `require()` and `check()` functions?
  • `require()`: Checks its arguments. If the condition is false, it throws an `IllegalArgumentException`. Used to validate arguments passed to a function or constructor.
  • `check()`: Checks the state of the object. If the condition is false, it throws an `IllegalStateException`. Used to validate the state of the object before performing an operation.
What is the difference between `println()` and `print()`?
  • `println()`: Prints the output followed by a newline character.
  • `print()`: Prints the output without a newline character.
What is string templating in Kotlin?
  • String templating allows you to embed variables or expressions directly within a string literal using the `$` symbol.
    val name = "Alice"
    val age = 30
    println("My name is $name and I am $age years old.") // My name is Alice and I am 30 years old.
    println("The sum of 2 and 3 is ${2 + 3}") // The sum of 2 and 3 is 5
How do you define a multiline string in Kotlin?
  • You can define a multiline string using triple quotes (`"""..."""`).
    val multilineString = """
        This is a
        multiline string
        in Kotlin.
        """
What is the purpose of the `trimIndent()` and `trimMargin()` functions for multiline strings?
  • `trimIndent()`: Removes the common minimal indentation from all lines of a multiline string.
  • `trimMargin()`: Removes a leading prefix (by default, `|`) and whitespace up to that prefix from each line. You can specify a different margin prefix.
What is the `Pair` and `Triple` class?
  • `Pair`: A data class that holds two values (of potentially different types).
    val pair = Pair("key", 123)
    val (key, value) = pair // Destructuring
  • `Triple`: A data class that holds three values.
    val triple = Triple("a", 1, true)
    val (x, y, z) = triple // Destructuring
What is the `use()` function? When is it used?
  • The `use()` function is an extension function on objects that implement the `Closeable` interface. It executes a block of code and automatically closes the resource afterwards, even if exceptions occur. It's similar to the try-with-resources statement in Java.
    File("example.txt").inputStream().use { inputStream ->
        // Read from inputStream
    } // inputStream is automatically closed
What is the difference between `List` and `List`?
  • `List`: In Kotlin, `List` is by default immutable and covariant in `T`. So `List` is effectively the same as `List`. It means you can only read elements of type `T` from the list, but you can assign a `List` to a variable of type `List`.
  • `List`: Explicitly declares that the `List` is covariant in `T`. This is redundant for the standard `List` interface because it's already declared this way, but it's important for understanding variance.
What is the difference between `MutableList` and `MutableList`?
  • `MutableList`: A mutable list. It's neither covariant nor contravariant in `T` because you can both add (`in`) and get (`out`) elements of type `T`.
  • `MutableList`: Declares that the `MutableList` is contravariant in `T`. This means you can only add elements of type `T` or its subtypes to the list, and you can assign a `MutableList` to a variable of type `MutableList`. You cannot read elements of type `T` (you can only read `Any?`). This is less common in practice compared to `out`.
How do you define a custom getter and setter for a property?
  • You can define custom getter and setter blocks after the property declaration using the `get()` and `set(value)` syntax.
    class Person {
        var name: String = ""
            get() {
                println("Getting name")
                return field // Access the backing field
            }
            set(value) {
                println("Setting name to $value")
                field = value // Modify the backing field
            }
    }
What is the `this` keyword in Kotlin?
  • `this` refers to the current receiver object.
    • In a class member function or property accessor, `this` refers to the instance of the class.
    • In an extension function, `this` refers to the instance of the receiver type.
    • In a lambda with a receiver, `this` refers to the receiver object of the lambda.
    You can use labeled `this` (`this@label`) to refer to a specific outer scope in nested constructs.
What is the purpose of the `annotations` keyword?
  • The `annotations` keyword is used to specify where an annotation should be applied when annotating properties or constructor parameters. For example, `@field:Ann`, `@property:Ann`, `@param:Ann`. This is needed because a single declaration in Kotlin might correspond to multiple elements in the compiled Java bytecode (e.g., a property becomes a field, a getter, and a setter).
What is the difference between a constructor parameter and a property declared in the primary constructor?
  • A constructor parameter (without `val` or `var`) is only available within the `init` blocks and secondary constructors for initialization. It does not become a property of the class instance.
    class Person(name: String) { // name is a constructor parameter
        init {
            println("Name is $name") // name is accessible here
        }
        // Cannot access 'name' outside init blocks or secondary constructors
    }
  • A property declared in the primary constructor (with `val` or `var`) becomes a member property of the class instance and is accessible throughout the class and from outside (if public).
    class Person(val name: String) { // name is a property
        fun greet() {
            println("Hello, $name") // name is accessible here
        }
    }
How does Kotlin handle checked exceptions from Java?
  • Kotlin doesn't have checked exceptions. When calling Java code that declares checked exceptions, Kotlin treats them as unchecked exceptions. You are not required to catch or declare them, but you can still use `try-catch` blocks if you want to handle them.