Kotlin | Вопросы собесов
2.56K subscribers
30 photos
972 links
Download Telegram
🤔 Как можно гарантировать быструю отрисовку чатов?

Чтобы чат работал быстро и без лагов, нужно:
Оптимизировать RecyclerView (ViewHolder, DiffUtil, Payloads).
Минимизировать работу в UI Thread (использовать Coroutines, Paging 3).
Оптимизировать загрузку изображений (Glide, Coil).
Использовать Room + Flow / LiveData для офлайн-кеша.

🚩Оптимизация `RecyclerView`

🟠Используем `ListAdapter` + `DiffUtil`
Вместо notifyDataSetChanged() лучше использовать ListAdapter, который сам обновляет только изменённые сообщения.
class ChatAdapter : ListAdapter<Message, ChatViewHolder>(DIFF_CALLBACK) {

companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Message>() {
override fun areItemsTheSame(oldItem: Message, newItem: Message): Boolean =
oldItem.id == newItem.id

override fun areContentsTheSame(oldItem: Message, newItem: Message): Boolean =
oldItem == newItem // data class сравнит автоматически
}
}
}


🟠Используем `Payloads` для частичного обновления
Если, например, только статус сообщения изменился, не нужно перерисовывать всё сообщение!
override fun getChangePayload(oldItem: Message, newItem: Message): Any? {
return if (oldItem.status != newItem.status) "STATUS_CHANGED" else null
}


Пример в onBindViewHolder()
override fun onBindViewHolder(holder: ChatViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.contains("STATUS_CHANGED")) {
holder.updateStatus() // Обновляем только статус
} else {
super.onBindViewHolder(holder, position, payloads)
}
}


🚩Оптимизируем загрузку сообщений (Coroutines, Paging 3)

Запросы в БД в Worker Thread (Coroutines + Room)
fun loadMessages(): Flow<List<Message>> = messageDao.getMessages()


Используем Paging 3 для подгрузки сообщений
val pager = Pager(
config = PagingConfig(pageSize = 20),
pagingSourceFactory = { messageDao.getPagedMessages() }
).flow.cachedIn(viewModelScope)


🚩Загружаем изображения асинхронно (Glide, Coil)
Если чат содержит аватары или изображения, они должны загружаться лениво и в фоновом потоке.
Glide.with(context)
.load(user.avatarUrl)
.circleCrop()
.placeholder(R.drawable.placeholder)
.into(imageView)


🚩Используем Room для офлайн-кеша

Чтобы сообщения не загружались из сети при каждом запуске, сохраняем их в Room.
@Dao
interface MessageDao {
@Query("SELECT * FROM messages ORDER BY timestamp DESC")
fun getMessages(): Flow<List<Message>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertMessages(messages: List<Message>)
}


🚩Оптимизируем `RecyclerView` для плавного скролла

Используем setHasFixedSize(true), если сообщения не меняют размер
recyclerView.setHasFixedSize(true)


Используем setItemViewCacheSize(), чтобы кэшировать ViewHolder
recyclerView.setItemViewCacheSize(20)


Отключаем анимации RecyclerView (если лагает)
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что известно про методы, которые перерисовывают View?

– invalidate() — помечает View на перерисовку (вызов onDraw).
– requestLayout() — вызывает перерасчёт размеров и размещения (onMeasure, onLayout).
– postInvalidate() — отложенная перерисовка из не-UI потока.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В чем разница между работой с list и работой с sequence?

🚩Основные структуры работы с коллекциями

🟠List
Является жадной коллекцией, что означает, что все операции над элементами выполняются немедленно и целиком. Когда вы применяете функции к списку, такие как map, filter и т.д., все элементы проходят через каждую функцию сразу же. В данном примере все элементы списка numbers сначала умножаются на 2, затем фильтруются. Вся работа выполняется сразу для каждого элемента.
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers
.map { it * 2 }
.filter { it > 5 }
println(result) // Output: [6, 8, 10]


🟠Sequence
Является ленивой коллекцией. Это означает, что элементы обрабатываются по мере необходимости. Функции, применяемые к последовательности, создают цепочку операций, которая выполняется только при обращении к элементам. В этом примере numbers сначала преобразуется в Sequence. Операции map и filter создают цепочку, которая выполняется только при преобразовании обратно в List с помощью toList(). Это позволяет избежать лишних операций и обрабатывать элементы по мере необходимости.
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.asSequence()
.map { it * 2 }
.filter { it > 5 }
.toList()
println(result) // Output: [6, 8, 10]


🚩Различия

🟠Жадность против ленивости
List: Все элементы обрабатываются сразу при вызове функций.
Sequence: Элементы обрабатываются по мере необходимости, что может повысить эффективность.

🟠Производительность
List: Подходит для небольших коллекций, где выполнение всех операций сразу не критично.
Sequence: Эффективен для больших коллекций или длинных цепочек операций, так как уменьшает количество промежуточных коллекций.

🟠Использование памяти
List: Может использовать больше памяти из-за создания промежуточных коллекций.
Sequence: Уменьшает использование памяти, обрабатывая элементы по одному.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Каким образом осуществлять проверки на null?

Kotlin предлагает:
- операторы безопасного вызова (?.);
- оператор Элвиса (?:);
- явную проверку через if (x != null);
- requireNotNull или !! — для гарантии не-null (но может выбросить исключение).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В каких случаях наследование полезно, а в каких нет?

Это один из ключевых принципов объектно-ориентированного программирования (ООП). Оно позволяет одному классу (наследнику) унаследовать свойства и методы другого класса (родителя). Это мощный инструмент, но использовать его нужно с умом, чтобы избежать лишней сложности и проблем с поддержкой кода.

🚩Когда наследование полезно

🟠Для повторного использования кода
Если у вас есть несколько классов, которые имеют общие свойства или методы, наследование позволяет вынести эти общие элементы в родительский класс. Это помогает избежать дублирования кода.

   open class Animal(val name: String) {
fun eat() {
println("$name ест.")
}
}

class Dog(name: String) : Animal(name) {
fun bark() {
println("$name лает.")
}
}

class Cat(name: String) : Animal(name) {
fun meow() {
println("$name мяукает.")
}
}

// Использование:
val dog = Dog("Бобик")
dog.eat() // "Бобик ест."
dog.bark() // "Бобик лает."


🟠Для создания иерархий классов (например, типологии)
Наследование отлично подходит для задач, связанных с созданием деревьев типов или категорий. Например, у вас есть базовый класс Shape (Форма), и от него наследуются конкретные фигуры: Circle, Rectangle, Triangle.

   open class Shape {
open fun draw() {
println("Рисуется форма.")
}
}

class Circle : Shape() {
override fun draw() {
println("Рисуется круг.")
}
}

class Rectangle : Shape() {
override fun draw() {
println("Рисуется прямоугольник.")
}
}

// Использование:
val shapes: List<Shape> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}


🟠Для использования полиморфизма
Полиморфизм позволяет использовать родительские классы для работы с объектами наследников. Это полезно, если у вас есть методы, которые работают с разными типами объектов, но с единым интерфейсом. Вызов методов draw() у всех объектов, не задумываясь об их конкретном типе, благодаря полиморфизму.

   fun render(shape: Shape) {
shape.draw()
}

render(Circle()) // "Рисуется круг."
render(Rectangle()) // "Рисуется прямоугольник."


🟠Когда концептуально существует отношение "is-a" (является)
Если наследник действительно является вариантом (подтипом) родительского класса, наследование логично. Например: Собака является животным (Dog is an Animal). Прямоугольник является фигурой (Rectangle is a Shape).

🚩Когда наследование не полезно

🟠Когда отношение "is-a" отсутствует
Если объекты не имеют строгого отношения "является", наследование использовать нельзя. Например: Класс Car (Машина) и Boat (Лодка) лучше объединить через общие свойства (интерфейсы или композицию), чем делать лодку наследником машины.

   open class Bird {
open fun fly() {
println("Птица летит.")
}
}

class Penguin : Bird() {
override fun fly() {
throw UnsupportedOperationException("Пингвин не умеет летать!")
}
}


🟠Когда нужно смешивать много разных поведений
Если ваш класс должен обладать несколькими несвязанными функциями, лучше использовать композицию или интерфейсы вместо наследования.

   class Car : Engine, Wheels

Используйте композицию:

   class Car {
private val engine = Engine()
private val wheels = Wheels()
}


🟠Сложные иерархии классов
Если вы создаете слишком глубокие или разветвленные иерархии, код становится сложным для понимания, тестирования и изменения. Например, изменение в базовом классе может неожиданно затронуть всех наследников.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🤔1
🤔 Как называется лейаут, в котором объекты могут наслаиваться друг на друга?

Такой тип называется FrameLayout в Android или Box в Jetpack Compose. Он позволяет размещать элементы поверх друг друга, управляя позицией вручную или через выравнивание.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Основные отличия Android 4 (Ice Cream Sandwich / Jelly Bean / KitKat) и Android 5 (Lollipop)

Android 5.0 Lollipop стал огромным обновлением по сравнению с Android 4.x, изменив интерфейс, архитектуру и производительность.

🟠Новый интерфейс — Material Design
Android 4: использовал Holo UI (чёрно-серый стиль с минимализмом).
Android 5: представил Material Design — новый визуальный стиль с тенями, анимациями и цветными кнопками.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add"
app:backgroundTint="@color/colorAccent"/>


🟠Новый движок ART вместо Dalvik
Android 4: использовал Dalvik (Just-in-Time, JIT) — код компилировался во время работы приложения.
Android 5: перешёл на ART (Ahead-of-Time, AOT) — код компилируется один раз при установке.

🟠Новый способ работы с уведомлениями
Android 4: уведомления были простыми и появлялись только в статус-баре.
Android 5: представил Heads-up Notifications (всплывающие уведомления).
val notification = NotificationCompat.Builder(context, "channel_id")
.setContentTitle("Новое сообщение")
.setContentText("Привет, как дела?")
.setPriority(NotificationCompat.PRIORITY_HIGH) // Heads-up уведомление
.setSmallIcon(R.drawable.ic_message)
.build()


🟠Поддержка 64-битных процессоров
Android 4: только 32-битная архитектура.
Android 5: появилась поддержка 64-битных процессоров, что улучшило производительность и работу с памятью.

🟠Улучшенное управление питанием — Project Volta
Android 4: не имел централизованного контроля за энергопотреблением.
Android 5: представил Project Volta, который продлевает жизнь батареи.
val jobScheduler = getSystemService(JobScheduler::class.java)
val jobInfo = JobInfo.Builder(1, ComponentName(this, MyJobService::class.java))
.setRequiresCharging(true) // Выполнить только при зарядке
.build()
jobScheduler.schedule(jobInfo)


🟠Новый режим многозадачности (Recent Apps)
Android 4: старые приложения в "Недавних" выглядели как список окон.
Android 5: приложения теперь отображаются в виде стопки карт (Card Stack UI).

🟠Улучшенная безопасность
Шифрование устройства включено по умолчанию (Android 5).
SELinux (Security-Enhanced Linux) теперь работает в принудительном (enforcing) режиме.
Добавлен Smart Lock → автоматическая разблокировка устройства по Bluetooth / Wi-Fi / лицу.

🟠Поддержка API для Wear OS и TV
Android 5 впервые добавил поддержку Android Wear (умные часы) и Android TV.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊3🤔1
🤔 Как можно писать RecyclerView, чтобы он кэшировал наперёд?

1. Использовать setItemViewCacheSize для кэширования определённого количества элементов.
2. Включить предзагрузку данных с помощью RecyclerView.OnScrollListener.
3. Реализовать Prefetching через LinearLayoutManager или RecyclerView.LayoutManager.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
🤔 Как в Ретрофите в гет методе поставить атрибут в путь в контретом месте?

В Retrofit, чтобы передать значение в определенное место URL-адреса для GET-запроса, нужно использовать аннотацию @Path. Эта аннотация позволяет заменить параметр в строке пути на значение, переданное в метод.

🚩Как работает `@Path`?

1. Внутри аннотации @GET вы указываете строку пути, содержащую плейсхолдеры в фигурных скобках ({}).
2. Аннотация @Path связывает переменную метода с плейсхолдером в URL.
3. Когда метод вызывается, значение переменной подставляется вместо плейсхолдера в URL.

Допустим, у вас есть API с эндпоинтом
https://api.example.com/users/{id}/details


Настраиваем интерфейс Retrofit
interface ApiService {

@GET("users/{id}/details")
suspend fun getUserDetails(
@Path("id") userId: String
): Response<UserDetails>
}


Теперь, когда вы вызываете этот метод, вы можете передать значение для id, и Retrofit автоматически подставит его в URL
val apiService = retrofit.create(ApiService::class.java)

suspend fun fetchUserDetails() {
val userId = "123" // Пример значения
val response = apiService.getUserDetails(userId)
if (response.isSuccessful) {
println("User details: ${response.body()}")
} else {
println("Error: ${response.errorBody()?.string()}")
}
}


🚩Дополнительные аспекты использования `@Path`

🟠Как работают несколько параметров?
Вы можете использовать несколько плейсхолдеров в пути и связать их с несколькими параметрами с помощью @Path. Например:
API-эндпоинт
https://api.example.com/users/{userId}/posts/{postId}


Интерфейс Retrofit
interface ApiService {

@GET("users/{userId}/posts/{postId}")
suspend fun getPostDetails(
@Path("userId") userId: String,
@Path("postId") postId: String
): Response<PostDetails>
}


Вызов метода
val response = apiService.getPostDetails("123", "456")
// URL: https://api.example.com/users/123/posts/456


🟠Что если переменная null или пустая?
Если вы передаете null или пустую строку в @Path, это вызовет ошибку, так как Retrofit требует обязательного значения для всех параметров пути. Убедитесь, что передаваемое значение всегда валидно.

🟠Как избежать ошибок при форматировании?
Если значение пути может содержать символы, требующие экранирования (например, пробелы, специальные символы), Retrofit автоматически обработает это с помощью кодировки URL. Это делает использование @Path безопасным.
@GET("search/{query}")
suspend fun search(
@Path("query") searchQuery: String
): Response<SearchResults>


Если вы вызовете метод с поисковым запросом
apiService.search("hello world!")
// Retrofit закодирует URL: https://api.example.com/search/hello%20world%21


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как создать handler thread?

Для этого создаётся новый поток (HandlerThread), запускается, затем получаем Looper и создаём Handler. Этот Handler будет отправлять задачи в новый поток. Такой подход позволяет выполнять фоновую работу без блокировки главного потока.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В чём отличие Dalvik Virtual Machine и ART(Android Run Time)?

Dalvik Virtual Machine (DVM) и Android Runtime (ART) — это две среды выполнения для запуска Android-приложений. DVM использовалась в ранних версиях Android, в то время как ART пришла на замену DVM, начиная с Android 5.0 (Lollipop). Основное различие между ними заключается в способе выполнения кода и производительности.

🚩Основные отличия между Dalvik и ART

🟠Тип выполнения кода
Dalvik Virtual Machine (DVM): Just-In-Time (JIT) компиляция
Dalvik использует JIT-компиляцию (Just-In-Time), что означает, что код приложения компилируется в машинный код во время выполнения (runtime).
Когда приложение запускается, DVM интерпретирует байт-код (.dex-файлы), а при необходимости компилирует часть кода "на лету" для повышения производительности.
Этот подход требует дополнительных ресурсов во время работы приложения, что увеличивает задержки (лаг) при запуске и потребляет больше CPU и батареи.
ART использует AOT-компиляцию (Ahead-Of-Time), которая компилирует весь код приложения в машинный код заранее — во время установки приложения.
Это устраняет необходимость интерпретации и JIT-компиляции во время работы приложения, что снижает нагрузку на процессор и улучшает производительность.

🟠Производительность
Dalvik (DVM):
Поскольку JIT-компиляция происходит во время работы приложения, это создает дополнительную нагрузку на процессор и замедляет выполнение.
Производительность ниже из-за частой интерпретации кода.
ART:
Благодаря AOT-компиляции приложения запускаются быстрее и работают плавнее.
Потребление ресурсов (CPU, батарея) значительно ниже, поскольку интерпретация и компиляция кода уже выполнены на этапе установки.

🟠Ускорение запуска приложений
Dalvik (DVM):
Приложения запускаются медленнее, так как DVM интерпретирует код во время каждого запуска.
ART:
Приложения запускаются быстрее, так как код уже компилирован в машинный код на этапе установки.

🟠Потребление батареи
Dalvik (DVM):
Потребляет больше батареи из-за того, что JIT-компиляция выполняется постоянно во время работы приложения.
ART:
Более энергоэффективен, так как большая часть работы выполнена заранее, и процессор не нагружается так сильно.

🟠Время установки приложения
Dalvik (DVM):
Приложения устанавливаются быстрее, так как код не компилируется заранее.
ART:
Приложения устанавливаются медленнее, так как на этапе установки выполняется AOT-компиляция.
Например, установка приложения в ART может занимать больше времени, чем в DVM, из-за компиляции кода.

🟠Память
Dalvik (DVM):
DVM использует меньше памяти на устройстве, так как код компилируется только во время работы приложения, и машинный код не сохраняется.
ART:
AOT-компиляция увеличивает размер приложения, так как компилированный машинный код сохраняется на устройстве. Это требует больше места в памяти.

🟠Отладка и инструменты
Dalvik (DVM):
Ограниченные возможности отладки, так как JIT-компиляция не предоставляет доступа к заранее оптимизированному коду.
ART:
ART позволяет разработчикам использовать более продвинутые инструменты отладки (например, профилирование исполнения) и лучше анализировать производительность приложений.

🟠Совместимость
Dalvik (DVM):
Dalvik был изначально разработан для устройств с ограниченными ресурсами (медленные процессоры, малый объем оперативной памяти).
Приложения работали в основном в условиях ограниченного оборудования.
ART:
ART ориентирован на современные устройства с мощными процессорами и большим объемом памяти.
Он лучше справляется с современными требованиями приложений.

🚩Пример различий

🟠На Dalvik (DVM):
- Установка быстрая.
- При запуске приложения DVM интерпретирует и компилирует код. Это требует времени и ресурсов.
- Приложение может работать медленно из-за интерпретации кода в реальном времени.
🟠На ART:
- Установка занимает больше времени, так как код компилируется сразу.
- Запуск приложения быстрый, потому что код уже готов к исполнению.
- Приложение работает плавно, так как отсутствует необходимость компиляции во время выполнения.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как преобразовать горячий поток в холодный в RxJava и наоборот?

Горячий поток можно симулировать, используя Subject с несколькими подписчиками. Холодный поток можно создать заново или применить defer, чтобы каждый подписчик получал независимый поток.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое SharedFlow и чем он отличается от StateFlow?

В Kotlin Flow есть два специальных вида потоков для управления состоянием и передачей данных:
StateFlow — используется для хранения и отслеживания состояния.
SharedFlow — используется для многократной отправки данных нескольким подписчикам.
Теперь разберёмся в деталях.

🚩Что такое SharedFlow?

SharedFlow — это горячий (hot) поток, который можно использовать для передачи данных нескольким подписчикам. Он не хранит состояние и просто раздаёт значения подписчикам в реальном времени.

🚩Как работает SharedFlow?

- Позволяет многим подписчикам получать одни и те же данные.
- Может буферизировать значения (хранить их для новых подписчиков).
- Может повторять последние значения (replay) для новых подписчиков.
- Может накапливать данные и работать как очередь событий.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
val sharedFlow = MutableSharedFlow<Int>(replay = 2) // Будет повторять последние 2 значения для новых подписчиков

launch {
for (i in 1..5) {
sharedFlow.emit(i)
delay(100)
}
}

launch {
delay(150) // Подписываемся чуть позже
sharedFlow.collect { println("Первый подписчик получил: $it") }
}

launch {
delay(300) // Подписываемся ещё позже
sharedFlow.collect { println("Второй подписчик получил: $it") }
}
}


🚩Что такое StateFlow?

StateFlow — это поток, который всегда хранит одно последнее значениЕ. Он идеально подходит для представления состояния (например, UI-состояния в MVVM).
- Всегда содержит одно актуальное значение.
- Если новое значение не отличается от текущего, оно не отправляется подписчикам.
- Новый подписчик сразу получает текущее значение.
- Можно думать о StateFlow как о LiveData, но для корутин.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
val stateFlow = MutableStateFlow(0) // Начальное состояние

launch {
delay(200) // Подписываемся позже
stateFlow.collect { println("Подписчик получил: $it") }
}

delay(100)
stateFlow.value = 1 // Меняем состояние
stateFlow.value = 2 // Меняем состояние
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Что такое sealed классы и зачем они нужны?

Sealed классы в Kotlin позволяют ограничить набор подклассов, которые могут быть созданы для этого класса, обеспечивая строгую иерархию. Это полезно для работы с данными, которые могут иметь конечное число состояний, таких как результаты операций (успех, ошибка, загрузка). Sealed классы упрощают обработку данных в `when` выражениях, так как компилятор проверяет, что все возможные подклассы учтены. Они помогают сделать код более безопасным и понятным.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
🤔 Сколько типов инстансов у nothing?

Nothing — это специальный bottom type (нижний тип), который означает:
Функция никогда не возвращает результат (throw, error()).
Код после Nothing недостижим.
fun fail(): Nothing {
throw IllegalStateException("Ошибка!") // Никогда не возвращает значение
}


🚩Почему `Nothing` не имеет инстансов?

Все классы в Kotlin могут иметь инстансы (объекты), кроме Nothing.
Nothing нельзя создать (instantiate), потому что он не имеет конструктора.
Любая переменная типа Nothing просто не существует.
val x: Nothing = Nothing() //  Ошибка: у Nothing нет конструктора


🚩Где используется `Nothing`?

Используется в throw
fun fail(): Nothing = throw IllegalArgumentException("Ошибка")


Используется в TODO()
fun getData(): String {
TODO("Функция ещё не реализована")
}


Используется в if-else, где один вариант throw
fun getValue(x: Int): String {
return if (x > 0) "Позитивное число" else throw IllegalArgumentException("Отрицательное!")
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5👍2
🤔 Почему плохо закидывать все зависимости в один компонент? Как это сделать по-другому?

Минусы одного глобального компонента:
- Переполнение графа зависимостей.
- Потеря контроля над временем жизни объектов.
- Трудности в поддержке и тестировании.
Лучше использовать иерархию компонентов:
- AppComponent для глобальных зависимостей (например, Retrofit, Room).
- ActivityComponent, FragmentComponent для зависимостей с ограниченным временем жизни.
- Использовать Subcomponent или Component dependencies.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 Какие есть ограничения у sealed классов?

Sealed классы являются мощным инструментом для создания ограниченных иерархий классов, что обеспечивает безопасное и предсказуемое использование классов.

🚩Ограничения sealed классов

🟠Все подтипы должны быть определены в том же файле
Все классы, которые наследуются от sealed класса, должны быть определены в одном файле с самим sealed классом. Это ограничение введено для обеспечения полной контрольной иерархии и предотвращения добавления новых подтипов в другом месте.
sealed class Result {
class Success(val data: String) : Result()
class Error(val exception: Exception) : Result()
}


🟠Sealed класс не может быть интерфейсом
Sealed класс не может быть интерфейсом. Если вам нужно определить ограниченную иерархию интерфейсов, вы должны использовать обычные интерфейсы и sealed классы вместе.

🟠Sealed класс не может быть абстрактным классом напрямую
Хотя sealed классы и являются абстрактными по своей природе (их нельзя напрямую инстанцировать), они не могут быть явно помечены как abstract.

🟠Ограничение видимости
Sealed классы и их подтипы не могут быть private. Они должны быть либо public, либо internal, чтобы их можно было использовать в рамках всего файла иерархии.

🟠Sealed классы не поддерживают наследование от других классов
Sealed класс не может наследоваться от другого класса, кроме Any. Это связано с тем, что sealed классы уже имеют специфическое предназначение и их иерархия должна быть полностью определена в одном месте.

🟠Могут использоваться только для классов и объектов, но не интерфейсов
Sealed классы могут быть использованы для классов и объектов, но не могут использоваться для определения интерфейсов.

sealed class Operation {
class Addition(val value: Int) : Operation()
class Subtraction(val value: Int) : Operation()
object NoOp : Operation()
}

fun execute(operation: Operation, base: Int): Int {
return when (operation) {
is Operation.Addition -> base + operation.value
is Operation.Subtraction -> base - operation.value
Operation.NoOp -> base
}
}


🚩Плюсы

Компилятор знает все подтипы
Это позволяет использовать исчерпывающие выражения when, которые не требуют блока else, если все подтипы покрыты.
Полный контроль над иерархией классов
Это делает код более предсказуемым и безопасным.
Поддержка сопоставления с образцом (pattern matching)
Это делает код более читаемым и менее подверженным ошибкам.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊5👍3
🤔 Как работает HashMap?

HashMap в Kotlin хранит пары ключ-значение и использует хеширование для быстрого поиска и вставки элементов. Каждый ключ хешируется, и результат хеш-функции определяет, где в таблице будет храниться соответствующее значение. В случае коллизий (когда два ключа имеют одинаковый хеш) HashMap использует цепочки или другие методы для хранения нескольких значений в одной корзине. Это обеспечивает доступ к элементам за среднее время O(1).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
🤔 В какой момент генерируется код при использовании SQLite?

Код для работы с SQLite в Android не генерируется автоматически — разработчик сам пишет SQL-запросы и управляет БД.

Но если используется Room (ORM для SQLite), то код генерируется на этапе компиляции с помощью Annotation Processing (kapt) или KSP.

🚩Обычный SQLite – без генерации кода

Если используешь SQLiteOpenHelper, то код не генерируется, ты сам пишешь SQL-запросы:
class MyDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "mydb", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
}

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS users")
onCreate(db)
}
}


🚩Room – код генерируется при компиляции

Если используешь Room, код автоматически генерируется во время компиляции с помощью Annotation Processing (kapt) или KSP.
Ты пишешь аннотации (@Entity, @Dao, @Database).
Room-аннотации анализируются при компиляции (kapt / KSP).
Генерируется SQL-код и DAO-методы.
@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)

@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAllUsers(): List<User>

@Insert
fun insert(user: User)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}


🚩Когда именно генерируется код Room?

Если используешь kapt (Annotation Processor) или KSP, то код генерируется во время компиляции.
dependencies {
implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1") // Кодогенерация
}


Пример с KSP (быстрее kapt)
dependencies {
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1") // Кодогенерация
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чем отличие горячих и холодных потоков?

– Холодные (cold) — начинают генерировать данные только после подписки (например, Flow, Observable).
– Горячие (hot) — генерируют данные независимо от подписчиков (например, SharedFlow, LiveData, Broadcast).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1