Irbisa · Cheatsheet

Kotlin OOP & Functional

Classes, data classes, sealed classes, objects, extension functions, scope functions

Junior Developer · 25 questions

  1. 01

    What is a data class, what does the compiler generate for it, and what are its limits?

    A data class is a class meant to hold values: from the properties of its primary constructor the compiler generates equals()/hashCode(), toString(), copy() and componentN() for destructuring.

    data class User(val id: Long, val name: String) {  var lastSeen: Long = 0          // body property: not part of equals/toString} val a = User(1, "Alice")val b = a.copy(name = "Alicia")   // new instance, id kept…
  2. 02

    What are sealed classes and sealed interfaces, and how do they make when exhaustive?

    A sealed class or interface has a closed set of direct subtypes known at compile time, so a when over it covers every case without else, and the compiler flags every such when the day you add a new subtype.

    sealed interface LoginState {  data object Idle : LoginState  data object Loading : LoginState  data class Success(val userId: Long) : LoginState  data class Error(val message: String) : LoginState}…
  3. 03

    What is an enum class, can its constants hold data, and when is a sealed class the better choice?

    An enum class is a fixed set of named instances of one type; constants can have constructor properties, methods and even per-constant overrides, but they all share one shape and each exists exactly once.

    enum class OrderStatus(val label: String) {  PENDING("Waiting"),  SHIPPED("On the way"),  UNKNOWN("Unknown");   companion object {…
  4. 04

    What is the difference between an abstract class and an interface in Kotlin, and why are classes final by default?

    An abstract class can hold state and has a constructor, but you extend only one; an interface holds no state, but a class can implement many, and both may contain implemented methods.

    interface Logger {  val tag: String                                  // no backing field allowed  fun log(msg: String) = println("[$tag] $msg")    // default body} abstract class BaseRepo(protected val baseUrl: String) {   // state + constructor…
  5. 05

    What are the four OOP principles, and what is the difference between overloading and overriding?

    The four principles are encapsulation, abstraction, inheritance and polymorphism; overloading is one name with different parameters, chosen at compile time, overriding is a subclass replacing a method, chosen at runtime by the real type.

  6. 06

    Which visibility modifiers does Kotlin have, and what does internal actually cover?

    Kotlin has public (the default), private, protected and internal; internal means visible anywhere in the same module (one set of files compiled together, such as a Gradle module's main source set).

  7. 07

    What are extension functions and properties, how are they compiled, and can they access private members?

    An extension adds a function or property to an existing type without changing its source; it compiles to a static method that takes the receiver as its first parameter, so it sees only the receiver's public and internal members, never private ones.

    // StringExt.ktfun String.initials(): String =  split(" ").filter { it.isNotBlank() }.joinToString("") { it.first().uppercase() } val String.isEmail: Boolean          // no backing field, computed on each read  get() = contains('@')…
  8. 08

    What is an infix function, and what rules must it follow?

    An infix function is a member or extension with exactly one parameter that can be called without the dot and parentheses: a to b instead of a.to(b).

    infix fun Int.percentOf(total: Int): Int = this * total / 100 val tax = 20 percentOf 150             // 30val pair = "id" to 42                  // Pair("id", 42)for (i in 0 until 10 step 2) print(i)  // 02468…
  9. 09

    What is the difference between the scope functions let, run, with, apply and also?

    They all run a block on an object and differ in two things: whether the object is this or it inside the block, and whether the call returns the object itself or the block's result.

    val length: Int? = name?.let { it.length }          // null-safe transform val intent = Intent(context, DetailActivity::class.java).apply {  putExtra("id", id)                                 // this = Intent  addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)}…
  10. 10

    What is the equals/hashCode contract, and why must you override both together?

    Equal objects must return equal hashCode values; override only equals and a HashSet or HashMap usually looks in the wrong bucket and fails to find an object equal to one already stored.

    open class User(val id: Long, var displayName: String) {  override fun equals(other: Any?): Boolean {    if (this === other) return true    if (other == null || javaClass != other.javaClass) return false    return id == (other as User).id        // entity: id only, displayName is mutable  }…
  11. 11

    What is the difference between a nested class and an inner class in Kotlin, and how can inner leak a Fragment?

    A nested class has no reference to an outer instance (Java's static nested class), while an inner class stores a hidden reference to it, so it can use outer members but also keeps the outer object alive.

    class Outer(private val name: String) {  class Nested {    fun describe() = "no access to name"            // no outer reference  }  inner class Inner {    fun describe() = "inner of ${this@Outer.name}"   // holds Outer…
  12. 12

    What does this code print, and what does each of apply, also, let and run return?

    val list = mutableListOf(1, 2) val a = list.apply { add(3) }val b = list.also { it.add(4) }.sizeval c = list.let { it.add(5) }val d = list.run { size } println(a)println("$b $c $d")

    It prints [1, 2, 3, 4, 5] and then 4 true 5: apply and also return the receiver, while let and run return the last expression of the block.

  13. 13

    What is the difference between class, object and companion object, and is an object singleton thread-safe?

    A class is a blueprint you instantiate as often as you like, an object is a singleton declared and created in one step, and a companion object is a singleton attached to a class that holds what Java would make static.

    object Analytics {                    // one instance, created on first access  private val events = mutableListOf<String>()  @Synchronized fun track(e: String) { events += e }   // state still needs guarding} class User private constructor(val name: String) {…
  14. 14

    In what order do the companion object, property initializers, init blocks and a secondary constructor run?

    The companion object is initialized once, when the class is first loaded; for each instance the superclass is built first, then property initializers and init blocks run in the order they appear in the class body, and a secondary constructor's own body runs last.

    class User(val name: String, age: Int) {  companion object { init { println("0 companion") } }   // once per class load   val greeting = "Hello, $name".also { println("1 initializer") }   init {…
  15. 15

    What does this code print, and what does it tell you about copy() on a data class?

    data class Cart(val owner: String, val items: MutableList<String>) val original = Cart("ann", mutableListOf("tea"))val copy = original.copy(owner = "bob")copy.items += "milk" println(original.items)println(original == copy)println(original.items === copy.items)

    It prints [tea, milk], false, true: copy() is shallow, so both carts point to the same MutableList, and adding to the copy's list changes the original.

  16. 16

    What does this code print, and how are extension functions dispatched compared to member functions?

    open class Shape { open fun name() = "Shape" }class Circle : Shape() { override fun name() = "Circle" } fun Shape.label() = "shape"fun Circle.label() = "circle"fun Circle.name() = "extension" fun main() {  val s: Shape = Circle()  println(s.name())  println(s.label())  println(Circle().name())}

    It prints Circle, shape, Circle: members are dispatched by the runtime type, extensions by the declared type, and a member always beats an extension with the same signature.

  17. 17

    Find the bug: finished downloads never leave active, and activeCount() only grows.

    data class Download(val url: String, var progress: Int = 0) class DownloadTracker {    private val active = HashSet<Download>()     fun start(url: String): Download =        Download(url).also { active += it }     fun onProgress(d: Download, percent: Int) { d.progress = percent }    fun finish(d: Download) { active.remove(d) }    fun activeCount() = active.size}

    progress is a constructor var of a data class, so it is part of hashCode; once onProgress changes it, remove(d) looks under a different hash, finds nothing, and every finished download stays in active forever.

  18. 18

    Why does for (d in downloads) if (d.done) downloads.remove(d) throw ConcurrentModificationException on one thread, and how do you fix it?

    The list's iterator remembers a modification count when the loop starts and checks it on every next(); removing through the list itself changes that count, so the next step of the loop usually throws.

    data class Download(val id: Long, val done: Boolean) val downloads = mutableListOf(  Download(1, done = true), Download(2, done = false), Download(3, done = true))…
  19. 19

    What is a callable reference (::), and what does a bound reference like viewModel::onSubmit capture?

    :: turns an existing function, property or constructor into a function value; a bound reference (obj::method) captures that object when the reference is created, while an unbound one (Type::method) takes the receiver as its first parameter.

    class SubmitViewModel {  fun onSubmit(text: String) { /* ... */ }} val vm = SubmitViewModel()val bound: (String) -> Unit = vm::onSubmit                       // receiver fixed here…
  20. 20

    How does operator overloading work in Kotlin, and how do you make +, [] and in work on your own type?

    Each operator maps to a function with a fixed name, and marking that function operator is the whole opt-in: a + b calls a.plus(b), a[i] calls a.get(i), x in a calls a.contains(x).

    @JvmInlinevalue class Money(val cents: Long) : Comparable<Money> {  operator fun plus(other: Money) = Money(cents + other.cents)  operator fun times(factor: Int) = Money(cents * factor)  override fun compareTo(other: Money) = cents.compareTo(other.cents)}…
  21. 21

    What is the difference between a Sequence pipeline and a List pipeline, and when does asSequence() hurt?

    A Sequence runs each element through the whole chain lazily, only when a terminal operation like toList() or first() runs, while a List chain finishes every step for all elements and allocates an intermediate list per step.

    val firstThree = generateSequence(1) { it + 1 }      // infinite source  .filter { println("filter $it"); it % 2 == 0 }  .map { println("map $it"); it * it }  .take(3)  .toList()                                      // terminal: starts the work// prints filter 1, filter 2, map 2, filter 3, filter 4, map 4, ... one element at a time…
  22. 22

    What is SAM conversion, and what does fun interface add for Kotlin interfaces?

    SAM conversion lets you pass a lambda where an interface with a single abstract method is expected; Java interfaces get it automatically, a Kotlin interface needs the fun interface modifier.

    fun interface Validator {  fun validate(s: String): Boolean} val notBlank = Validator { it.isNotBlank() }       // SAM constructorfun check(v: Validator, input: String) = v.validate(input)…
  23. 23

    Why is composition often preferred over inheritance, and how does Kotlin's by interface delegation support it?

    Inheritance ties a subclass to its parent's implementation, so a change in the base class can break subclasses; composition keeps the collaborator behind an interface, and class LoggingRepo(inner: Repo) : Repo by inner makes the compiler write every forwarding method for you.

    class User(val id: Long) interface Repo {  fun load(id: Long): User  fun refresh()}…
  24. 24

    What are the SOLID principles? Give an Android example of each and one violation.

    SOLID is five rules for classes that stay easy to change: single responsibility, open/closed, Liskov substitution, interface segregation and dependency inversion.

  25. 25

    Is Singleton an anti-pattern on Android, and how do you keep one instance without a global object?

    Singleton is not an anti-pattern by itself, but a global object that code calls directly is hidden global state: callers do not declare it, tests cannot swap it, and it lives for the whole process.