PPrepLearn
Progress Β· 0/10 sections

🟒 🟒 Kotlin Basics & Fundamentals

5 min read Β· Notion


🧠 Core Language

What is Kotlin? How is it different from Java?

Kotlin is a statically typed, JVM-targeting language developed by JetBrains. Key differences from Java:

  • Null Safety: Kotlin distinguishes nullable (String?) and non-nullable (String) types at the compiler level.
  • Conciseness: Data classes, extension functions, and smart casts eliminate boilerplate.
  • Coroutines: First-class async/concurrency support.
  • No checked exceptions: Kotlin does not force you to catch exceptions.
  • Functional programming: Lambdas, higher-order functions, and collection operators are idiomatic.
  • Interoperability: 100% compatible with Java β€” you can call Java code from Kotlin and vice versa.
What is null safety in Kotlin?

Kotlin's type system distinguishes nullable from non-nullable types at compile time:

var a: String = "hello"   // cannot be null
var b: String? = null      // can be null
b?.length                  // safe call β€” returns null if b is null
b ?: "default"             // Elvis operator β€” fallback value
b!!.length                 // non-null assertion β€” throws NPE if null

This eliminates most NullPointerExceptions at compile time.

val vs var
  • val = immutable reference (like Java final). The object itself can still be mutated.
  • var = mutable reference, can be reassigned.
val x = 10       // cannot reassign x
var y = 20       // y can be reassigned
y = 30           // OK
What is the Elvis operator ?:?

The Elvis operator provides a default value when an expression is null:

val length = name?.length ?: 0
// If name is null, length = 0; otherwise length = name.length
What are extension functions?

Extension functions let you add methods to existing classes without inheriting from them:

fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}
"racecar".isPalindrome() // true

They're resolved statically and don't modify the original class.

What is a data class?

A data class auto-generates equals(), hashCode(), toString(), copy(), and componentN() functions:

data class User(val name: String, val age: Int)
val u1 = User("Ashu", 28)
val u2 = u1.copy(age = 29)

Best for immutable value objects, DTOs, and domain models.

Sealed class vs Enum class

Enum: Fixed set of constant instances, all of the same type.

Sealed class: Fixed set of subclasses, each can hold different data. Great for modeling states.

sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

Use sealed classes when subclasses need to carry different payloads.

What is a companion object?

A companion object is a singleton tied to a class β€” it replaces Java's static members:

class MyClass {
    companion object {
        const val TAG = "MyClass"
        fun create() = MyClass()
    }
}
MyClass.TAG
MyClass.create()
Scope functions: let, apply, also, run, with
FunctionReceiverReturnsCommon Use
letitLambda resultNull checks, transformations
applythisReceiver objectObject configuration / builder
alsoitReceiver objectSide effects, logging
runthisLambda resultExecute block, compute result
withthisLambda resultCall multiple methods on object
val user = User().apply {
    name = "Ashu"
    age = 28
}
user?.let { println(it.name) }

πŸ” Higher-Order Functions & Lambdas

What is a higher-order function?

A function that takes a function as parameter or returns a function:

fun operate(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)
operate(3, 4) { a, b -> a + b } // 7
What is an inline function?

Inline functions copy their body to the call site at compile time, eliminating lambda object allocation overhead:

inline fun measure(block: () -> Unit) {
    val start = System.nanoTime()
    block()
    println(System.nanoTime() - start)
}

Use for small functions that accept lambdas to reduce memory pressure.

Difference between inline, noinline, and crossinline
  • inline: The entire lambda is inlined at call site.
  • noinline: Prevents a specific lambda parameter from being inlined (when you want to store it).
  • crossinline: Allows inlining but prevents non-local returns inside the lambda.
inline fun example(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    inlined()       // inlined
    notInlined()    // stored as object
}
Lazy vs lateinit
  • lazy: Value is computed once on first access. Thread-safe by default. Used with val.
  • lateinit: Mutable var that will be assigned before use. Not null-safe β€” throws if accessed before init.
val config: Config by lazy { loadConfig() }
lateinit var binding: ActivityMainBinding

Use lazy for read-only computed values; lateinit for DI-injected or lifecycle-bound mutable references.


πŸ“š Collections

Mutable vs Immutable collections

Kotlin separates read-only interfaces (List, Set, Map) from mutable ones (MutableList, MutableSet, MutableMap):

val list = listOf(1, 2, 3)           // read-only
val mutableList = mutableListOf(1, 2) // mutable
mutableList.add(3)

Read-only collections are not thread-safe β€” use ConcurrentHashMap or coroutine-friendly structures for shared state.

map, filter, reduce, flatMap
val nums = listOf(1, 2, 3, 4, 5)
nums.filter { it % 2 == 0 }     // [2, 4]
nums.map { it * 2 }              // [2, 4, 6, 8, 10]
nums.reduce { acc, it -> acc + it } // 15
listOf(listOf(1,2), listOf(3,4)).flatMap { it } // [1, 2, 3, 4]

flatMap maps each element to a collection and flattens the result into a single list.

What is the Result class in Kotlin?

Result<T> is a standard library class that models success or failure:

val result = runCatching { riskyOperation() }
result.onSuccess { println(it) }
result.onFailure { println(it.message) }

Great alternative to try/catch chains in functional-style code.

Related