Kotlin | Вопросы собесов
2.57K subscribers
28 photos
965 links
Download Telegram
🤔 Как называется лейаут в котором объекты могут наслаиваться друг на друга?

В Android для наложения (перекрытия) элементов друг на друга используется FrameLayout или Box (в Jetpack Compose).

🚩FrameLayout (в XML и View)

FrameLayout — это контейнер, в котором все вложенные элементы располагаются в левом верхнем углу, но при этом могут накладываться друг на друга.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/background" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Наложенный текст"
android:textSize="24sp"
android:textColor="#FFFFFF"
android:layout_gravity="center"/>
</FrameLayout>


🚩Box (в Jetpack Compose)

В Jetpack Compose аналогом FrameLayout является Box. Он также позволяет располагать элементы друг над другом.
Box(
modifier = Modifier.fillMaxSize()
) {
Image(
painter = painterResource(id = R.drawable.background),
contentDescription = "Фон",
modifier = Modifier.fillMaxSize()
)

Text(
text = "Наложенный текст",
fontSize = 24.sp,
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}


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

Нет, MutableState в Compose не гарантирует потокобезопасность.
Если несколько потоков одновременно модифицируют состояние, это может привести к состоянию гонки (race condition).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
🤔 Можешь рассказать отличия и в каких случаях их использовать list set map?

В Kotlin есть три основных типа коллекций:
List — упорядоченный список элементов.
Set — множество уникальных элементов.
Map — коллекция пар "ключ-значение".

🚩List — упорядоченный список

List — это коллекция, в которой можно хранить дубликаты, а элементы доступны по индексу.
val numbers = listOf(1, 2, 3, 4, 5) // Immutable (нельзя изменять)
val mutableNumbers = mutableListOf(1, 2, 3) // Можно изменять

mutableNumbers.add(4) // Добавляем элемент
mutableNumbers.removeAt(1) // Удаляем элемент по индексу

println(mutableNumbers) // [1, 3, 4]


🚩Set — множество уникальных элементов

Set — это коллекция, в которой нет дубликатов.
val numbers = setOf(1, 2, 3, 3, 4, 5) // Дубликаты удаляются автоматически
println(numbers) // [1, 2, 3, 4, 5]

val mutableNumbers = mutableSetOf(1, 2, 3)
mutableNumbers.add(3) // Не добавится, потому что уже есть
mutableNumbers.add(4) // Добавится

println(mutableNumbers) // [1, 2, 3, 4]


🚩Map — коллекция пар "ключ-значение"

Map — это структура данных, в которой каждому ключу соответствует одно значение.
val userMap = mapOf(
1 to "Alice",
2 to "Bob",
3 to "Charlie"
) // Immutable

val mutableUserMap = mutableMapOf(1 to "Alice", 2 to "Bob")
mutableUserMap[3] = "Charlie" // Добавляем новый ключ-значение
mutableUserMap.remove(1) // Удаляем элемент по ключу

println(mutableUserMap) // {2=Bob, 3=Charlie}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
🤔 Как рисует Compose на экране?

Jetpack Compose использует асинхронный рендеринг, который проходит несколько этапов:
1. Композиция – анализ и построение дерева UI.
2. Составление Layout – расчет размеров и позиций элементов.
3. Рендеринг – отрисовка UI через Canvas и GPU.


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

Да, объём стека изменится, а вот объём кучи останется неизменным (но нагрузка на неё увеличится).

🚩Что происходит со стеком при создании нового потока?

Стек (Stack) — это область памяти для локальных переменных и вызовов функций.
- У каждого потока (Thread) есть свой отдельный стек.
- Размер стека фиксирован и устанавливается при создании потока.
- Чем больше потоков, тем больше памяти выделяется под стеки.
Если размер стека 1 МБ, и мы создаём 100 потоков, то под стеки уйдёт 100 МБ памяти.

🚩Что происходит с кучей (Heap) при создании потоков?

Куча (Heap) — это область памяти для объектов.
- Куча общая для всех потоков.
- Новый поток не создаёт отдельную кучу, он использует ту же самую.
- Но больше потоков → больше создаваемых объектов → больше нагрузка на сборщик мусора (GC).
Вывод: Объём кучи не меняется автоматически, но может быстрее заполняться.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Как рисует Compose на iOS?

Jetpack Compose for iOS (в рамках Kotlin Multiplatform) использует:
- Skia – для отрисовки графики (через Compose Runtime).
- Metal/OpenGL – для работы с GPU.
- UIKit – для взаимодействия с iOS-компонентами.


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

В Android можно создавать интерфейс без XML с помощью Jetpack Compose или программного кода (View в Kotlin/Java).

🚩Jetpack Compose — современный декларативный подход

Jetpack Compose — это новый способ создания UI в Android без XML, основанный на Kotlin.
@Composable
fun GreetingScreen() {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Привет, мир!", fontSize = 24.sp)
Button(onClick = { println("Кнопка нажата") }) {
Text("Нажми меня")
}
}
}

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GreetingScreen()
}
}
}


🚩Создание UI через View в Kotlin (старый способ)

Если Jetpack Compose не подходит, можно создать интерфейс программно, без XML, используя стандартные View.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Создаём контейнер (LinearLayout)
val layout = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}

// Создаём текст
val textView = TextView(this).apply {
text = "Привет, мир!"
textSize = 24f
setTextColor(Color.BLACK)
}

// Создаём кнопку
val button = Button(this).apply {
text = "Нажми меня"
setOnClickListener {
textView.text = "Кнопка нажата!"
}
}

// Добавляем элементы в контейнер
layout.addView(textView)
layout.addView(button)

// Устанавливаем UI
setContentView(layout)
}
}


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

Каждый MutableState имеет подписчиков (observers), которые автоматически уведомляются об изменениях.
При обновлении значения, Compose отправляет сигнал о необходимости рекомпозиции тем элементам, которые используют это состояние.


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

В interface можно определять свойства (val или var), но их реализация зависит от того, есть ли у них get и set.

🚩Свойства без реализации (только объявление)

В интерфейсе можно объявить val (только для чтения) или var (для чтения и записи), но без инициализации.
interface User {
val name: String // Объявляем свойство, но не реализуем
var age: Int // Может изменяться, но без начального значения
}


Использование в классе
class Person(override val name: String) : User {
override var age: Int = 25 // Обязательно реализовать
}


🚩Свойства с `get` (реализация в интерфейсе)

Можно задать кастомный геттер прямо в интерфейсе.
interface User {
val name: String
val greeting: String
get() = "Привет, $name!" // Реализация внутри интерфейса
}


Использование в классе
class Person(override val name: String) : User

fun main() {
val user = Person("Андрей")
println(user.greeting) // Привет, Андрей!
}


🚩Свойства с `get` и `set` (нельзя реализовать в интерфейсе)

Если в интерфейсе объявить var, то нельзя задать реализацию геттера и сеттера — их должен реализовать класс.
interface User {
var age: Int // Только объявление, без реализации
}


Использование в классе
class Person : User {
override var age: Int = 30 // Реализуем свойство
}


🚩Свойства с `get() = field` (не работает в интерфейсе!)

В interface нельзя использовать field (бэкинг-поле), потому что у интерфейсов нет состояния.
interface User {
var age: Int
get() = field // Ошибка! Нельзя использовать `field` в интерфейсе
set(value) { field = value }
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Какой будет возвращаемый тип у suspend-функции, которая возвращает String, после компиляции?

Возвращаемый тип становится Any, потому что функция может вернуть либо String, либо корутину в приостановленном состоянии.


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

В Kotlin нет switch, но его заменяет более мощный оператор when.

Простой пример (аналог switch-case)
fun getDayName(day: Int): String {
return when (day) {
1 -> "Понедельник"
2 -> "Вторник"
3 -> "Среда"
4 -> "Четверг"
5 -> "Пятница"
6 -> "Суббота"
7 -> "Воскресенье"
else -> "Некорректный день"
}
}

fun main() {
println(getDayName(3)) // Среда
}


when без else (если учтены все случаи)
fun getStatus(code: Int): String = when (code) {
200 -> "OK"
404 -> "Not Found"
500 -> "Server Error"
}


Несколько значений в одном case
fun isWeekend(day: Int): Boolean {
return when (day) {
6, 7 -> true // Оба значения работают как один case
else -> false
}
}


🚩`when` как альтернатива `if-else`

Можно проверять логические выражения, а не просто числа.
fun checkNumber(x: Int): String {
return when {
x < 0 -> "Отрицательное число"
x == 0 -> "Ноль"
x > 0 -> "Положительное число"
else -> "Ошибка"
}
}


Использование when без аргумента
fun describe(obj: Any): String {
return when {
obj is String -> "Это строка"
obj is Int -> "Это число"
obj is Boolean -> "Это логический тип"
else -> "Неизвестный тип"
}
}


🚩`when` в качестве выражения

when можно использовать как выражение, возвращая результат:
val message = when (val code = 404) {
200 -> "OK"
404 -> "Not Found"
else -> "Unknown"
}
println(message) // Not Found


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🤯1👾1
🤔 Как создать suspend-функцию?

Создать suspend-функцию можно, добавив suspend перед fun.
suspend-функции нельзя вызывать напрямую из обычных функций, только из корутин.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🤯5😁3🔥1
🤔 С чем связаны ограничения на запуск сервисов?

Google постоянно ограничивает работу сервисов в Android, чтобы:
Уменьшить расход батареи
Оптимизировать использование памяти
Защитить пользователя от фоновых процессов, "убивающих" производительность

🟠Запрет на запуск сервисов в фоне (Android 8+)
Сервис нельзя запустить из фона, если приложение не активно.
startService(Intent) выдаст ошибку, если приложение не на переднем плане.
1. Использовать Foreground Service (с уведомлением).
2. Использовать JobIntentService / WorkManager.
class MyForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, "channelId")
.setContentTitle("Сервис работает")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()

startForeground(1, notification) // Запускаем сервис в Foreground
}

override fun onBind(intent: Intent?): IBinder? = null
}


🟠Запрет на старт сервисов из фона (Android 10+)
Если сервис запущен из фона (например, через BroadcastReceiver), он не запустится.
Исключение – если сервис работает в Foreground или с AlarmManager.
1. Использовать WorkManager (лучший вариант).
2. Использовать Foreground Service с уведомлением.
3. Использовать AlarmManager для периодических задач.

🟠Ограничение работы сервисов в спящем режиме (Doze Mode, Android 6+)
В спящем режиме (Doze Mode) система отключает сервисы.
Приложения не могут выполнять фоновые задачи.
Использовать Foreground Service.
Использовать JobScheduler / WorkManager.
Использовать Firebase Cloud Messaging (FCM) для пробуждения приложения.

🟠Запрет на работу сервисов после закрытия приложения (Android 9+)
Если пользователь смахнул приложение из списка недавних, фоновые сервисы будут убиты.
Решение → Foreground Service + startForeground() или JobScheduler.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Откуда можно запускать suspend-функции?

suspend-функции можно запускать только из корутин.
Можно вызывать их из:
1. launch {} или async {} в CoroutineScope
2. Другое suspend-функции`
3. runBlocking {} (для тестирования в main-потоке)


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🤯2🔥1😁1🤔1
🤔 Можно ли создать анонимный класс внутри inline функции?

Анонимный класс внутри inline-функции будет инстанцироваться при каждом вызове функции, что ломает цель inline — избавляться от накладных расходов на объекты.

🚩Что такое анонимный класс в Kotlin?

Анонимный класс — это класс без имени, который можно создать с помощью object : Interface {}.
interface ClickListener {
fun onClick()
}

fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата!")
}
}

listener.onClick()
}


🚩Анонимный класс в `inline`-функции

Если мы создадим анонимный класс внутри inline-функции, он будет создаваться каждый раз при вызове функции!
inline fun setClickListener(action: () -> Unit) {
val listener = object : ClickListener {
override fun onClick() {
action()
}
}
listener.onClick()
}

fun main() {
setClickListener { println("Нажато!") }
}


Ошибка компиляции
Inline function cannot contain object expressions


🚩Как обойти ограничение?

Если нужно использовать inline, можно передавать лямбду вместо анонимного класса.
Рабочий вариант
inline fun setClickListener(noinline action: () -> Unit): ClickListener {
return object : ClickListener {
override fun onClick() {
action()
}
}
}

fun main() {
val listener = setClickListener { println("Нажато!") }
listener.onClick()
}


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

Рекомпозиция управляется Android Choreographer, который работает с VSYNC.
Процесс:
1. Изменение MutableState ставит флаг "UI требует перерисовки".
2. Choreographer ждет ближайший VSYNC и запускает рекомпозицию.
3. Перерисовываются только измененные элементы UI.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2🤯1
🤔 Основные отличия 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
😁3👍2
🤔 Как функция определяет, что она suspend'илась?

Suspend-функция не блокирует поток, а передает управление Continuation-объекту.
Функция "узнает" о приостановке через:
- Фрейм Continuation: если вызвана другая suspend-функция, текущая приостанавливает свое выполнение.
- Корутинный диспетчер: приостановка возможна, если выполнение ушло в другой поток (withContext).
- Возвращение специального маркера COROUTINE_SUSPENDED: сигнализирует Kotlin Runtime, что выполнение отложено.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5🤯3
🤔 Свойства какого вида можно добавить как расширение?

В Kotlin можно добавлять свойства-расширения (extension properties), но только с кастомным get (геттером).

🚩Можно добавлять `val` с `get()`

Расширяемые свойства могут быть только вычисляемыми (val), потому что нельзя создать field внутри расширения.
val String.firstChar: Char
get() = this[0]

fun main() {
println("Kotlin".firstChar) // K
}


🚩`var` работает только с `get()` и `set()`

Для var нужно и get(), и set(), но всё равно нельзя использовать field.
var StringBuilder.lastChar: Char
get() = this[length - 1]
set(value) {
this.setCharAt(length - 1, value)
}

fun main() {
val sb = StringBuilder("Hello")
println(sb.lastChar) // o

sb.lastChar = '!'
println(sb) // Hell!
}


🚩Нельзя создавать свойства с `field`

Такой код НЕ скомпилируется!
var String.someProperty: String = "Default" // Ошибка!


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что известно про snapshot'ы?

Это механизм, позволяющий атомарно управлять состоянием.
Основные характеристики snapshot:
- Снимки (snapshots) позволяют фиксировать состояние в определенный момент времени.
- Внесенные изменения откладываются до момента коммита (commit).
- Работают аналогично транзакциям – можно откатить изменения, если они конфликтуют.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Зачем нужны Data Class и Sealed Classes?

Data Class и Sealed Class решают разные задачи и обеспечивают улучшения в организации кода, управлении состоянием и безопасности типов. Они вносят значительные упрощения и повышают читаемость кода в Kotlin-проектах.

🚩Зачем они нужны

Data Class предназначены для хранения данных и автоматически предоставляют ряд полезных методов, что упрощает разработку и уменьшает объем шаблонного кода. Основные причины использования Data Class:

🟠Сокращение кода
Автоматически генерирует методы equals(), hashCode(), и toString(), а также copy() и компонентные функции для объектов данных. Это избавляет от необходимости ручной реализации этих методов, что уменьшает количество кода и возможность ошибок.

🟠Упрощение передачи данных
Идеально подходят для передачи данных между различными частями приложения, например, между слоями в архитектуре MVVM или при передаче данных между активностями и фрагментами.

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

🚩Зачем они нужны

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

🟠Полное покрытие случаев в `when`
Гарантируют, что все возможные подтипы обработаны в выражениях when, что предотвращает ошибки во время выполнения из-за пропущенных случаев. Это упрощает управление состояниями и делает код более безопасным и предсказуемым.

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

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

data class User(val name: String, val age: Int)


Пример Sealed Class
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: Throwable) : Result()
}

fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success with data: ${result.data}")
is Result.Failure -> println("Failure with error: ${result.error.message}")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5