Kotlin is the primary language for Android development. According to Google Android Docs (2025), over 95% of new Android projects use Kotlin. Understanding Kotlin specifics — data class, sealed class, coroutines and scope functions — is a mandatory requirement for an Android developer.
Key Takeaways
Kotlin extends class concepts from Java. data class, sealed class, object and companion object — constructs that don't exist in Java or are implemented differently.
data class — a class that automatically generates equals(), hashCode(), toString(), copy() and componentN(). Ideal for data models (DTO, Entity). Requirements: at least one primary constructor parameter, val/var, cannot be open/sealed/inner. sealed class — a class with a restricted subclass hierarchy. All subclasses are known at compile time and declared in the same file. when expression with sealed class does not require an else branch. sealed interface (Kotlin 1.9+) — same as sealed class but allows multiple inheritance. object — a singleton, declared with the object keyword (not class). Thread-safe lazy initialization. companion object — static members of a class. Has a name (Companion by default). Can implement interfaces and have extension functions.
| Type | Description | Usage |
|---|---|---|
| data class | Automatic generation of equals, hashCode, toString, copy | Data models, DTO, Entity |
| sealed class | Restricted hierarchy, when checking | States, Result, UI State |
| sealed interface | sealed class + multiple inheritance | Events, Actions (Kotlin 1.9+) |
| object | Singleton (declarative) | Factory, Utility, Constants |
| companion object | Static class members | Factory Method, static constants |
| enum class | Enumeration with constants | States with a fixed set |
data class — for 90% of models. sealed class — for UI State and Result. object — for singletons. IT Sectr recommends sealed class for all screen states (Loading, Success, Error).
Sealed class — a powerful tool for modeling states. Example: UiState<T> with subclasses Loading, Success(data: T), Error(message: String). when expression guarantees handling all states. Sealed interface — for events and actions where multiple implementation is needed. Value class (Kotlin 1.5+) — a wrapper for a single value without overhead. Used for type-safe identifiers: @JvmInline value class UserId(val id: String).
Extension Function — adding a method to an existing class without inheritance. fun String.isEmail(): Boolean. Extension Property — same for properties (without backing field). Inline Function — inlining the function body at the call site — no call overhead. Used for higher-order functions with lambdas. Higher-Order Function — a function that takes or returns another function. Scope Functions — five functions for working with object context: let, run, with, apply, also.
let — context it, returns lambda result. Used for null-safe calls (?.let {}). apply — context this, returns context. For object configuration. run — context this, returns lambda result. For computations with context. also — context it, returns context. For side effects (logging). with — not an extension, context this, returns lambda result. For a group of operations on an object. Lambda — anonymous function. Destructuring Declaration — unpacking data class into variables: val (name, age) = person.
Inline function with reified — the only way to preserve a generic type in JVM. Used for: type-safe builders (Gson.fromJson<T>()), type checking (is T), getting the class (T::class). Crossinline and Noinline — modifiers for lambda parameters in inline functions. crossinline — forbids non-local return, noinline — forbids inlining a specific lambda. IT Sectr recommends inline + reified only when working with types; for regular higher-order functions, inline can increase bytecode size.
Coroutines — lightweight threads for asynchronous programming. They don't block the thread — they suspend. suspend function — a function that can be suspended without blocking the thread. Can only be called from a coroutine or another suspend function.
CoroutineScope — a scope for coroutines. Contains CoroutineContext (Job + Dispatcher). lifecycleScope — for Activity/Fragment (Lifecycle-aware). viewModelScope — for ViewModel (auto-cancellation on onCleared()). GlobalScope — global scope (not recommended for production — memory leaks). Dispatchers: Dispatchers.Main (UI), Dispatchers.IO (network/disk), Dispatchers.Default (CPU-intensive), Dispatchers.Unconfined (not recommended). launch — starts a coroutine: Job. async — starts with result: Deferred<T>. runBlocking — blocking bridge. withContext — switches Dispatcher inside a suspend function.
// Example coroutines: loading data in ViewModel
class MainViewModel : ViewModel() {
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
fun loadData() {
viewModelScope.launch {
_state.value = UiState.Loading
try {
val data = withContext(Dispatchers.IO) {
repository.fetchData()
}
_state.value = UiState.Success(data)
} catch (e: Exception) {
_state.value = UiState.Error(e.message)
}
}
}
}
// Parallel requests with async
suspend fun loadCombinedData(): CombinedData = coroutineScope {
val users = async(Dispatchers.IO) { api.getUsers() }
val posts = async(Dispatchers.IO) { api.getPosts() }
CombinedData(users.await(), posts.await())
}viewModelScope.launch — standard coroutine launch in ViewModel. withContext(Dispatchers.IO) — switching to IO thread for network request. Dispatchers determine on which thread pool the coroutine executes. IT Sectr recommends viewModelScope for ViewModel and lifecycleScope for Activity/Fragment.
reified — a type modifier in inline functions. Allows using the type: T::class, is T, as T. Without reified, Generics in JVM are erased (type erasure). inline fun + reified — a powerful combination for type-safe APIs. Destructuring Declaration — val (x, y) = point. Works for data class and types with componentN(). typealias — type alias: typealias Callback = (String) -> Unit. lateinit — deferred var initialization (for dependency injection). Not thread-safe. lazy — lazy val initialization (thread-safe by default). lazy(LazyThreadSafetyMode.NONE) — without synchronization. Extension function and Extension property — adding functionality without inheritance. Kotlin is a language where extension functions solve 90% of utility tasks.
Delegated Properties — delegating getter/setter to another object. Standard delegates: lazy, observable, vetoable, map. Custom delegate via getValue/setValue operators. by — delegation keyword: val name by lazy { loadName() }. Delegated properties are the foundation for ViewModel delegates (by viewModels()). IT Sectr recommends lazy for deferred initialization and Delegates.observable for observing changes.
| Feature | Description | Example |
|---|---|---|
| reified | Preserving generic type in inline | inline fun <reified T> parse(): T |
| destructuring | Unpacking data class | val (name, age) = person |
| typealias | Type alias | typealias Orders = List<Order> |
| lateinit | Deferred var initialization | lateinit var adapter: Adapter |
| lazy | Lazy val initialization | val config by lazy { load() } |
| delegated | Property delegation | var x by Delegates.observable(0) |
reified — for type-safe APIs. lazy — for deferred loading. delegated properties — for reusing property logic. IT Sectr recommends mastering all these features for effective Kotlin development.
Flow — cold asynchronous data stream in Kotlin. Emits values on demand by the collector. StateFlow — hot Flow with a single current value. Used in ViewModel for UI State. SharedFlow — hot Flow for events (one-shot events). flowOf, asFlow — creating Flow from collections. catch, retry, debounce — operators. stateIn, shareIn — converting cold Flow to hot. IT Sectr recommends StateFlow for UI State and SharedFlow for events (Snackbar, navigation).
class SearchViewModel : ViewModel() {
private val _query = MutableStateFlow("")
val suggestions: StateFlow<List<String>> = _query
.debounce(300)
.filter { it.length >= 2 }
.flatMapLatest { repository.search(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
fun onQueryChanged(query: String) {
_query.value = query
}
}kotlinx.serialization — Kotlin library for JSON serialization. @Serializable — annotation. @SerialName — custom field name. Supports: JSON, CBOR, ProtoBuf. Parcelize — @Parcelize for Android Parcelable (without manual implementation). Gson vs Moshi vs kotlinx: kotlinx.serialization — Kotlin-first, compile-safe, without reflection. Moshi — annotations + codegen. IT Sectr recommends kotlinx.serialization for new projects and Moshi for existing ones.
@Serializable
data class ApiResponse<T>(
val success: Boolean,
@SerialName("data") val data: T? = null,
val error: String? = null
)
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
// Deserialization
val json = Json { ignoreUnknownKeys = true }
val response = json.decodeFromString<ApiResponse<List<User>>>(jsonString)
println("Users: ${response.data?.size ?: 0}")Frequently Asked Questions
data class — for data (auto-generation of equals/hashCode/copy). sealed class — for type hierarchy (when checking). sealed interface — same with multiple inheritance.
let, run, with, apply, also. Differ by context (it vs this) and return value (result vs context). let — null safety, apply — configuration, also — side effects.
CoroutineScope — a scope for coroutines. lifecycleScope — for Activity, viewModelScope — for ViewModel. GlobalScope — not recommended.
reified — type preservation in inline functions. Allows T::class and is T. Only works with inline functions.
launch — without result. async — with result (Deferred). runBlocking — blocking bridge for main/test. withContext — Dispatcher switching.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.