Irbisa · Cheatsheet
Kotlin Advanced
Generics, inline/reified, delegates, DSL builders, type aliases
What is a
typealias, what does it compile to, and why does it not make your code type-safe?typealias UserId = Longtypealias ClickHandler = (position: Int) -> Unittypealias Cache<K> = MutableMap<K, ByteArray> fun ban(id: UserId) { /* ... */ }val orderId: Long = 42L…How does variance work in Kotlin generics (
out,in, star projection), and how does it map to Java's PECS?open class Animalclass Dog : Animal() interface Source<out T> { fun next(): T } // producerinterface Sink<in T> { fun put(item: T) } // consumer…Why does one
Loadingobject fit everyUiState<T>in this code, and what breaks if you removeout?sealed interface UiState<out T> { data object Loading : UiState<Nothing> data class Success<T>(val data: T) : UiState<T> data class Error(val cause: Throwable) : UiState<Nothing>} val users: UiState<List<User>> = UiState.Loadingval profile: UiState<User> = UiState.LoadingWhat is type erasure, and why does the
ischeck here not compile while the cast compiles and crashes later?fun handle(payload: Any) { if (payload is List<String>) { // compile error println(payload.first().length) } val names = payload as List<String> // warning: unchecked cast println(names.first().length) // ClassCastException here} handle(listOf(1, 2, 3))What does
inlinedo in Kotlin, what arenoinlineandcrossinlinefor, and when isinlinea bad idea?inline fun <T> measure(label: String, block: () -> T): T { val start = System.nanoTime() return block().also { println("$label: ${System.nanoTime() - start} ns") }} fun firstAdult(users: List<User>): User? {…This
inlinehelper does not compile: what is the compiler protecting you from, and what are the two ways to fix it?inline fun runOnUi(block: () -> Unit) { Handler(Looper.getMainLooper()).post { block() }} fun save() { runOnUi { if (isFinishing) return }}What is
reified, why does it only work withinlinefunctions, and can Java call such a function?inline fun <reified T : Activity> Context.launch(vararg extras: Pair<String, Any?>) { startActivity(Intent(this, T::class.java).putExtras(bundleOf(*extras)))} inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? = BundleCompat.getParcelable(this, key, T::class.java)…How do property delegates work, and when would you use
lazy,observable,vetoable,notNullorby map?class ProfileViewModel : ViewModel() { private val formatter by lazy { DateTimeFormatter.ofPattern("dd.MM.yyyy") } var name: String by Delegates.observable("") { _, old, new -> Log.d("Profile", "name: $old -> $new") }…Is
by lazythread-safe, and what do theLazyThreadSafetyModeoptions change?class ImageLoader(private val context: Context) { // Default SYNCHRONIZED: safe to read from any thread val client: OkHttpClient by lazy { buildOkHttpClient(context) } // Read only on the main thread, so skip the lock val placeholder: Drawable by lazy(LazyThreadSafetyMode.NONE) {…Find the bug:
add()puts the item in the cart, but the screen never re-renders; onlyclear()updates it.class CartStore(private val onChange: (List<String>) -> Unit) { var items: MutableList<String> by Delegates.observable(mutableListOf()) { _, _, new -> onChange(new) } private set fun add(item: String) { items.add(item) } fun clear() { items = mutableListOf() }}What is a value class in Kotlin, and in which cases does it still allocate a wrapper object?
@JvmInlinevalue class UserId(val raw: Long) @JvmInlinevalue class Email(val value: String) { init { require('@' in value) { "Invalid email: $value" } }…What do
@JvmStatic,@JvmOverloads,@JvmFieldand@JvmNamedo for Java callers of your Kotlin code?@file:JvmName("StringUtils")package com.app.util fun slugify(s: String) = s.lowercase().replace(' ', '-') // Java: StringUtils.slugify(...) class Config private constructor(val host: String) {…A property declared as non-null
Stringheld null at runtime and crashed: how did the null get past the compiler?How do annotations and code generation work in Kotlin: what does retention control, and why move from kapt to KSP?
@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.FUNCTION)annotation class Route(val path: String) // before: kapt(libs.room.compiler)// after: ksp(libs.room.compiler)Why does the compiler demand
@OptIn(...)before some API calls, and how do you declare an opt-in marker of your own?@RequiresOptIn( message = "Sync API is experimental and may change without notice.", level = RequiresOptIn.Level.ERROR,)@Retention(AnnotationRetention.BINARY)@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)…You made a public library function
inline: it no longer compiles against an internal helper, and old callers keep running the previous body. Why?internal val defaultJson = Json { ignoreUnknownKeys = true } // error: Public-API inline function cannot access non-public-API 'defaultJson'// inline fun <reified T> decode(text: String): T = defaultJson.decodeFromString(text) @PublishedApi…Find the bug: after a while, every other thread that calls
refresh()blocks on this lock forever.inline fun <T> ReentrantLock.guarded(block: () -> T): T { lock() return block().also { unlock() }} class TokenCache(private val api: Api) { private val lock = ReentrantLock() private var token: String? = null fun refresh() { lock.guarded { if (token != null) return // already fresh token = api.fetchToken() } }}Your
inline fun <reified T> fromJson(s: String): Treturns a list ofLinkedTreeMapforfromJson<List<User>>(): where exactly doesreifiedstop?// wrong: T::class is the raw class, the User argument is goneinline fun <reified T : Any> fromJson(json: String): T = gson.fromJson(json, T::class.java) val users: List<User> = fromJson(body)println(users[0].name) // ClassCastException: LinkedTreeMap cannot be cast to User…Two properties in one class declared
by sharedend up holding the same value: what doesbyactually store, and how should a delegate know which property it serves?class Setting<T>(private val default: T) : ReadWriteProperty<Any?, T> { private var cached: T? = null // state lives in the DELEGATE override fun getValue(thisRef: Any?, property: KProperty<*>): T = cached ?: default override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { cached = value }}…How do you build a type-safe DSL in Kotlin, and what does
@DslMarkerprotect you from?@DslMarkerannotation class MenuDsl data class Item(val title: String, val icon: Int?, val onClick: () -> Unit)data class Menu(val items: List<Item>)…Why does a smart cast survive
if (!s.isNullOrEmpty())but disappear through an identical helper you wrote yourself?@OptIn(ExperimentalContracts::class)fun String?.isValidToken(): Boolean { contract { returns(true) implies (this@isValidToken != null) } return this != null && length == 32}…What are context parameters in Kotlin, and what problem do they solve that an extra function argument does not?
interface Logger { fun info(msg: String) }interface Analytics { fun track(event: String) } context(logger: Logger)fun sync() { logger.info("sync started")…What changed with the K2 compiler and Kotlin 2.x, and what does it mean for an Android project?
sealed interface Response { data class Ok(val body: String) : Response data class Failure(val code: Int, val retryable: Boolean) : Response} // Guard condition in when (stable in 2.2)…