Irbisa · Шпаргалка
Продвинутый Kotlin
Дженерики, inline/reified, делегаты, DSL builder-ы, type alias
Что такое
typealias, во что он компилируется и почему он не делает код типобезопасным?typealias UserId = Longtypealias ClickHandler = (position: Int) -> Unittypealias Cache<K> = MutableMap<K, ByteArray> fun ban(id: UserId) { /* ... */ }val orderId: Long = 42L…Как работает вариантность в дженериках Kotlin (
out,in, star projection) и как это соотносится с PECS из Java?open class Animalclass Dog : Animal() interface Source<out T> { fun next(): T } // producerinterface Sink<in T> { fun put(item: T) } // consumer…Почему один объект
Loadingподходит для любогоUiState<T>в этом коде и что сломается, если убратьout?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.LoadingЧто такое стирание типов и почему проверка
isздесь не компилируется, а приведение компилируется и падает позже?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))Что делает
inlineв Kotlin, для чего нужныnoinlineиcrossinlineи когдаinlineстановится плохой идеей?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? {…Этот
inline-хелпер не компилируется: от чего вас защищает компилятор и какие есть два способа это исправить?inline fun runOnUi(block: () -> Unit) { Handler(Looper.getMainLooper()).post { block() }} fun save() { runOnUi { if (isFinishing) return }}Что такое
reified, почему он работает только сinline-функциями и можно ли вызвать такую функцию из Java?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)…Как работают делегаты свойств и когда использовать
lazy,observable,vetoable,notNullилиby 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") }…Потокобезопасен ли
by lazyи что меняют режимыLazyThreadSafetyMode?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) {…Найдите баг:
add()кладёт товар в корзину, но экран не перерисовывается; его обновляет толькоclear().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() }}Что такое value class в Kotlin и в каких случаях он всё равно создаёт объект-обёртку?
@JvmInlinevalue class UserId(val raw: Long) @JvmInlinevalue class Email(val value: String) { init { require('@' in value) { "Invalid email: $value" } }…Что дают Java-коду аннотации
@JvmStatic,@JvmOverloads,@JvmFieldи@JvmName?@file:JvmName("StringUtils")package com.app.util fun slugify(s: String) = s.lowercase().replace(' ', '-') // Java: StringUtils.slugify(...) class Config private constructor(val host: String) {…Свойство с не-null типом
Stringв рантайме оказалось null и уронило приложение: как null прошёл мимо компилятора?Как в Kotlin работают аннотации и кодогенерация: что определяет retention и зачем переходить с kapt на KSP?
@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.FUNCTION)annotation class Route(val path: String) // before: kapt(libs.room.compiler)// after: ksp(libs.room.compiler)Почему компилятор требует
@OptIn(...)перед вызовом некоторых API и как объявить свой opt-in маркер?@RequiresOptIn( message = "Sync API is experimental and may change without notice.", level = RequiresOptIn.Level.ERROR,)@Retention(AnnotationRetention.BINARY)@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)…Вы сделали публичную функцию библиотеки
inline: она перестала компилироваться с internal-хелпером, а старые вызывающие продолжают выполнять прежнее тело. Почему?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…Найдите баг: через какое-то время все остальные потоки, вызывающие
refresh(), навсегда блокируются на этом локе.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() } }}Ваша
inline fun <reified T> fromJson(s: String): TдляfromJson<List<User>>()возвращает списокLinkedTreeMap: где именно заканчиваются возможностиreified?// 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…Два свойства одного класса, объявленные как
by shared, в итоге хранят одно и то же значение: что на самом деле хранитbyи как делегату понять, какое свойство он обслуживает?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 }}…Как построить типобезопасный DSL в Kotlin и от чего защищает
@DslMarker?@DslMarkerannotation class MenuDsl data class Item(val title: String, val icon: Int?, val onClick: () -> Unit)data class Menu(val items: List<Item>)…Почему smart cast сохраняется после
if (!s.isNullOrEmpty()), но пропадает при вызове такого же хелпера, написанного вами?@OptIn(ExperimentalContracts::class)fun String?.isValidToken(): Boolean { contract { returns(true) implies (this@isValidToken != null) } return this != null && length == 32}…Что такое context parameters в Kotlin и какую проблему они решают, которую не решает дополнительный аргумент функции?
interface Logger { fun info(msg: String) }interface Analytics { fun track(event: String) } context(logger: Logger)fun sync() { logger.info("sync started")…Что изменилось с компилятором K2 и Kotlin 2.x и что это значит для Android-проекта?
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)…