Kotlin | Вопросы собесов
Открыть в Telegram
Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @easyoffer_adv
Больше2 465
Подписчики
+124 часа
-37 дней
-2230 день
Загрузка данных...
Похожие каналы
Облако тегов
Входящие и исходящие упоминания
---
---
---
---
---
---
Привлечение подписчиков
июль '26
июль '26
+17
в 0 каналах
июнь '26
+23
в 0 каналах
Get PRO
май '26
+20
в 0 каналах
Get PRO
апрель '26
+30
в 0 каналах
Get PRO
март '26
+32
в 0 каналах
Get PRO
февраль '26
+46
в 0 каналах
Get PRO
январь '26
+54
в 0 каналах
Get PRO
декабрь '25
+23
в 0 каналах
Get PRO
ноябрь '25
+42
в 0 каналах
Get PRO
октябрь '25
+38
в 0 каналах
Get PRO
сентябрь '25
+44
в 0 каналах
Get PRO
август '25
+47
в 0 каналах
Get PRO
июль '25
+35
в 0 каналах
Get PRO
июнь '25
+46
в 0 каналах
Get PRO
май '25
+55
в 0 каналах
Get PRO
апрель '25
+57
в 0 каналах
Get PRO
март '25
+110
в 1 каналах
Get PRO
февраль '25
+105
в 2 каналах
Get PRO
январь '25
+159
в 53 каналах
Get PRO
декабрь '24
+59
в 0 каналах
Get PRO
ноябрь '24
+70
в 0 каналах
Get PRO
октябрь '24
+210
в 42 каналах
Get PRO
сентябрь '24
+608
в 251 каналах
Get PRO
август '24
+147
в 0 каналах
Get PRO
июль '24
+203
в 54 каналах
Get PRO
июнь '24
+492
в 244 каналах
Get PRO
май '24
+563
в 189 каналах
Get PRO
апрель '24
+461
в 23 каналах
| Дата | Привлечение подписчиков | Упоминания | Каналы | |
| 22 июля | +2 | |||
| 21 июля | +1 | |||
| 20 июля | +2 | |||
| 19 июля | 0 | |||
| 18 июля | 0 | |||
| 17 июля | +1 | |||
| 16 июля | +1 | |||
| 15 июля | +1 | |||
| 14 июля | +1 | |||
| 13 июля | 0 | |||
| 12 июля | +1 | |||
| 11 июля | 0 | |||
| 10 июля | +2 | |||
| 09 июля | 0 | |||
| 08 июля | 0 | |||
| 07 июля | +1 | |||
| 06 июля | +1 | |||
| 05 июля | +1 | |||
| 04 июля | 0 | |||
| 03 июля | +1 | |||
| 02 июля | +1 | |||
| 01 июля | 0 |
Посты канала
🤔 Бывают ли случаи, когда нельзя использовать inline?
Функции в Kotlin могут быть объявлены как `inline` для уменьшения накладных расходов при вызове функций высшего порядка. Однако, `inline` нельзя использовать, если тело функции содержит код, который не может быть встроен, например, если функция содержит рекурсивные вызовы сама на себя.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
| 2 | 🤔 Как называется лейаут в котором объекты могут наслаиваться друг на друга?
В 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)
)
}
Ставь 👍 и забирай 📚 Базу знаний | 206 |
| 3 | 🤔 Как работает HashMap?
HashMap в Kotlin хранит пары ключ-значение и использует хеширование для быстрого поиска и вставки элементов. Каждый ключ хешируется, и результат хеш-функции определяет, где в таблице будет храниться соответствующее значение. В случае коллизий (когда два ключа имеют одинаковый хеш) HashMap использует цепочки или другие методы для хранения нескольких значений в одной корзине. Это обеспечивает доступ к элементам за среднее время O(1).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 264 |
| 4 | 🤔 Какое событие вызывается при нажатии юзера по экрану?
В Android при нажатии пользователя на экран вызывается событие ACTION_DOWN.
🚩Разбор работы событий касания
Android использует систему обработки касаний через MotionEvent. Когда пользователь касается экрана, система генерирует разные типы событий:
ACTION_DOWN – вызывается в момент первого касания.
ACTION_MOVE – вызывается, когда пользователь двигает палец по экрану.
ACTION_UP – вызывается, когда пользователь убирает палец с экрана.
ACTION_CANCEL – вызывается, если система прерывает касание (например, из-за входящего звонка).
🚩Как обрабатывать нажатие
Для обработки событий касания нужно переопределить метод onTouchEvent() в View или использовать setOnTouchListener().
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.d("TouchEvent", "Палец коснулся экрана")
return true
}
MotionEvent.ACTION_UP -> {
Log.d("TouchEvent", "Палец отпущен")
}
}
return super.onTouchEvent(event)
}
Можно также назначить слушатель
view.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
Log.d("TouchEvent", "Нажатие зафиксировано")
true
} else {
false
}
}
Ставь 👍 и забирай 📚 Базу знаний | 284 |
| 5 | 🤔 Как сделать так, чтобы подобные факапы не происходили в будущем?
Помогают чек-листы, юнит- и интеграционные тесты, контроль версий, код-ревью и документация. Также важно проводить ретроспективы и делать выводы из предыдущих ошибок.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 282 |
| 6 | 🤔 Опиши как лучше всего использовать модификаторы доступа в Java?
В Java есть 4 модификатора доступа, которые контролируют видимость полей, методов и классов:
🚩`private` – самый безопасный (использовать по умолчанию)
Поля почти всегда должны быть private, чтобы инкапсулировать данные. Методы тоже лучше делать private, если они не должны использоваться извне.
public class User {
private String name; // ✅ Доступен только внутри класса
public User(String name) {
this.name = name;
}
private void logUserAction() { // ✅ Только внутри класса
System.out.println(name + " совершил действие");
}
}
🚩`package-private` (без модификатора) – для классов внутри пакета
Используется, если класс не должен быть доступен за пределами пакета. Полезно для вспомогательных классов.
class FileHelper { // ✅ Доступен только в этом пакете
static void readFile(String path) {
System.out.println("Читаем файл: " + path);
}
}
🚩`protected` – для наследников, но не для всех
protected лучше использовать только в abstract классах и для методов, которые должны быть переопределены. Не используй protected для полей → нарушает инкапсуляцию!
abstract class Animal {
protected void makeSound() { // ✅ Наследники могут переопределять
System.out.println("Какой-то звук");
}
}
class Dog extends Animal {
@Override
protected void makeSound() {
System.out.println("Гав-гав!");
}
}
🚩`public` – только если класс/метод нужен везде
Класс можно делать public, только если он должен быть доступен во всех пакетах. Не делай поля public → нарушает инкапсуляцию!
public class UserManager { // ✅ Доступен везде
public void createUser() { // ✅ Можно вызывать в любом месте
System.out.println("Создание пользователя...");
}
}
🚩`final` и `static` – дополняем модификаторы
final class → класс нельзя наследовать.
final method → метод нельзя переопределить.
final field → переменная неизменяема (константа).
public final class MathUtils {
public static final double PI = 3.14159;
}
Ставь 👍 и забирай 📚 Базу знаний | 280 |
| 7 | 🤔 Как описать жизненный цикл composable функции?
Жизненный цикл composable функции начинается с вызова и создания UI, затем проходит через стадии "recomposition" (обновления при изменении состояния) и завершения (удаление из дерева). Compose управляет этим процессом автоматически, основываясь на состоянии и действиях пользователя.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 284 |
| 8 | 🤔 Что такое Extensions?
Термин "Extensions" (расширения) используется для обозначения функциональности, которая позволяет добавлять новые возможности к существующим классам без изменения их исходного кода. Этот концепт особенно популярен к примеру в Swift и Kotlin, где расширения используются для улучшения читаемости кода, упрощения его структуры и добавления удобных методов для работы с уже существующими типами данных.
🚩Расширения в Kotlin
Расширения позволяют добавить новую функциональность к классам, даже если исходный код этих классов недоступен для изменений (например, классы из стандартной библиотеки или сторонних библиотек). Это делается путём объявления функций или свойств, которые могут быть вызваны на экземплярах этих классов, как если бы они были частью оригинальных классов.
// Расширение класса String для проверки, пуста ли строка или состоит из пробелов
fun String.isNullOrBlank(): Boolean {
return this == null || this.trim().isEmpty()
}
// Использование расширения
val myString = " "
println(myString.isNullOrBlank()) // Выведет: true
🚩Расширения в Swift
Использует очень похожий подход, позволяя добавлять новые методы и вычисляемые свойства к существующим типам. Расширения могут даже добавлять новые протоколы к типам, что делает их чрезвычайно мощным инструментом в разработке под iOS и macOS.
// Расширение стандартного типа String для добавления метода инвертирования строки
extension String {
func reversedString() -> String {
return String(self.reversed())
}
}
let myString = "Hello"
print(myString.reversedString()) // Выведет: "olleH"
🚩Плюсы
➕Улучшение читаемости и организации кода
Расширения позволяют держать методы, связанные с определёнными типами данных, ближе к использованию этих типов.
➕Избегание наследования
Расширения предоставляют альтернативный способ добавления функциональности к классам без использования наследования, что помогает избежать избыточной иерархии и увеличения сложности системы.
➕Реиспользование кода
Методы, добавленные через расширения, могут быть легко переиспользованы в различных частях приложения.
Ставь 👍 и забирай 📚 Базу знаний | 299 |
| 9 | 🤔 Почему наследование может быть плохим или неудобным?
– Жёсткая связанность,
– трудности с изменениями и тестированием,
– сложно переопределить поведение выборочно,
– нарушается принцип единственной ответственности.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 288 |
| 10 | 🤔 Как класс-наследник оперирует функционалом класса-родителя?
Класс-наследник в Kotlin получает доступ к функционалу родительского класса через:
Наследование (:) — доступ ко всем open методам и свойствам.
Ключевое слово super — вызов методов родителя.
Переопределение (override) — изменение поведения.
🚩Наследование: доступ к методам и свойствам
Класс-наследник получает доступ ко всем open методам и свойствам родителя.
open class Animal(val name: String) {
fun eat() {
println("$name ест")
}
}
class Dog(name: String) : Animal(name)
fun main() {
val dog = Dog("Шарик")
dog.eat() // Шарик ест (метод унаследован)
}
🚩`super` – вызов методов родителя
Пример
open class Animal {
open fun makeSound() {
println("Животное издаёт звук")
}
}
class Dog : Animal() {
override fun makeSound() {
super.makeSound() // Вызываем метод родителя
println("Гав-гав!")
}
}
fun main() {
val dog = Dog()
dog.makeSound()
}
Вывод
Животное издаёт звук
Гав-гав!
🚩Переопределение (`override`)
Класс-наследник может изменить поведение родительского метода.
open class Animal {
open fun move() {
println("Животное двигается")
}
}
class Bird : Animal() {
override fun move() {
println("Птица летит") // Переопределяем метод
}
}
fun main() {
val bird = Bird()
bird.move() // Птица летит
}
🚩Вызов конструктора родителя (`super`)
Пример
open class Animal(val name: String)
class Dog(name: String, val breed: String) : Animal(name) {
fun info() {
println("Имя: $name, Порода: $breed") // Используем `name` из родителя
}
}
fun main() {
val dog = Dog("Бобик", "Лабрадор")
dog.info() // Имя: Бобик, Порода: Лабрадор
}
Ставь 👍 и забирай 📚 Базу знаний | 300 |
| 11 | 🤔 Какие виды библиотек знаешь и в чём их различия?
- Java/Kotlin библиотеки — чистый код, без ресурсов;
- Android libraries (AAR) — включают ресурсы, манифест, стили, и т.д.;
- Native C/C++ библиотеки (.so) — используется через JNI.
Различие — в составе и способе интеграции.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 300 |
| 12 | 🤔 Расскажи про инициализаторы в классах в Kotlin
В Kotlin инициализаторы используются для выполнения кода при создании экземпляра класса.
Первичный (primary) конструктор
Вторичные (secondary) конструкторы
Инициализационный блок (init)
🟠Первичный конструктор
Это основной способ инициализации класса в Kotlin. Он объявляется в заголовке класса.
class User(val name: String, val age: Int)
Если нужно выполнить дополнительную логику во время создания объекта, используют блок init:
class User(val name: String, val age: Int) {
init {
println("Создан пользователь: $name, возраст: $age")
}
}
🟠Вторичные конструкторы
Они объявляются с помощью constructor и нужны, если:
Нужно несколько способов создания объекта.
Надо вызвать другой конструктор (this(...)).
class User {
var name: String
var age: Int
constructor(name: String) {
this.name = name
this.age = 18 // Значение по умолчанию
}
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
Можно создать объект так
val user1 = User("Алекс") // возраст будет 18
val user2 = User("Иван", 25)
Если есть первичный конструктор, вторичный должен его вызывать через this(...)
class User(val name: String, val age: Int) {
constructor(name: String) : this(name, 18)
}
🟠`init` vs вторичный конструктор
init выполняется всегда при вызове первичного конструктора.
Вторичный конструктор создаёт альтернативный способ создания объекта.
class User(val name: String, val age: Int) {
init {
println("Создан пользователь: $name, возраст: $age")
}
constructor(name: String) : this(name, 18) {
println("Вызван вторичный конструктор")
}
}
Ставь 👍 и забирай 📚 Базу знаний | 314 |
| 13 | 🤔 Как называется action для IntentFilter после перезагрузки телефона?
Это Intent.ACTION_BOOT_COMPLETED. Его можно использовать в BroadcastReceiver, чтобы выполнять действия после запуска системы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 305 |
| 14 | 🤔 Как сборщик мусора понимает что обьект можно уничтожить?
Сборщик мусора (Garbage Collector, GC) в Android (и в JVM) использует анализ ссылок для определения, можно ли уничтожить объект.
🚩Объект считается "мёртвым", если на него нет доступных ссылок
GC работает по принципу "сборки мусора с поиском корней" (Tracing Garbage Collection).
GC ищет "корневые" объекты (Root Objects) – это объекты, к которым точно есть ссылка (например, статические переменные, локальные переменные текущего потока, объекты в стеке).
GC обходит все объекты, к которым есть ссылки (прямые или косвенные).
Если объект не связан с корневыми объектами, он считается "мусором" и удаляется.
fun main() {
var user: User? = User("Alice") // Создаём объект
user = null // Теперь на объект нет ссылок, GC его удалит
}
🚩Сборщик мусора использует "Mark & Sweep"
Метод Mark & Sweep – основной алгоритм работы GC.
Mark (Пометка) – GC помечает все достижимые объекты (к которым есть ссылки).
Sweep (Очистка) – GC удаляет непомеченные объекты (на которые нет ссылок).
Root → A → B
→ C
Недостижимые объекты (GC их удаляет)
Root → A → B (C больше недоступен)
C (GC удалит!)
🚩"Сборка поколений" (Generational GC)
Объекты делятся на молодые (Young) и старые (Old)
Young Generation – новые объекты (большинство умирает быстро).
Old Generation – "долго живущие" объекты (Activity, Singleton).
🚩Что GC НЕ удаляет? (Memory Leaks)
Утечки памяти (Memory Leaks) происходят, если на объект осталась ссылка, но он больше не нужен.
class Activity {
var button: Button? = null
}
var activity: Activity? = Activity() // Создаём объект
activity?.button = Button() // `Button` ссылается на `Activity`
activity = null // Activity нельзя удалить из-за ссылки на кнопку!
Ставь 👍 и забирай 📚 Базу знаний | 399 |
| 15 | 🤔 Чем data class отличается от обычного класса?
`data class` в Kotlin автоматически генерирует полезные методы, такие как `equals()`, `hashCode()`, `toString()`, и `copy()`, что делает его идеальным для хранения данных. Обычные классы не предоставляют такую функциональность по умолчанию, и все эти методы должны быть реализованы вручную. Data-классы используются для создания объектов, которые содержат только данные, без сложной логики. Это сокращает количество шаблонного кода и улучшает читаемость.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 389 |
| 16 | 🤔 В каких случаях используют atomic, в каких synchronized?
В Kotlin для работы с многопоточностью можно использовать atomic переменные или synchronized блоки. Они помогают избежать гонок данных (race conditions), когда несколько потоков одновременно изменяют одну и ту же переменную.
🚩Когда использовать `atomic` переменные?
Atomic переменные (AtomicInteger, AtomicLong, AtomicReference и др.) используются, когда нужно быстро и безопасно обновлять одно значение без блокировки потока.
Высокая производительность – атомарные операции работают быстрее, чем synchronized.
Потокобезопасность – операции выполняются без блокировок (lock-free).
Удобство – простые методы incrementAndGet(), compareAndSet() и т. д.
Работает только с одним значением – не подходит для сложных операций.
Не подходит для зависимых изменений (например, если изменение одной переменной зависит от другой).
import java.util.concurrent.atomic.AtomicInteger
val counter = AtomicInteger(0)
fun increment() {
counter.incrementAndGet() // Увеличиваем значение безопасно
}
🟠Когда использовать `synchronized`?
Используется, когда нужно защитить сразу несколько связанных операций от одновременного выполнения в нескольких потоках.
Подходит для сложных операций с несколькими переменными.
Работает с любыми типами данных.
Снижает производительность – потоки ждут, пока один поток закончит работу.
Может вызывать deadlock (взаимную блокировку).
val lock = Any()
var count = 0
fun increment() {
synchronized(lock) {
count++ // Только один поток может изменять `count` одновременно
}
}
Ставь 👍 и забирай 📚 Базу знаний | 409 |
| 17 | 🤔 Назовите конкретные критерии качества кода?
- Читаемость — понятные названия, форматирование.
- Поддерживаемость — легко править и расширять.
- Тестируемость — модульная структура, слабая связанность.
- Производительность — без лишних аллокаций, утечек.
- Соответствие архитектуре и best practices.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 401 |
| 18 | 🤔 В чём отличие АПК с подписью и без подписи?
APK (Android Package) — это архив с кодом приложения, ресурсами и манифестом.
Приложение должно быть подписано, чтобы его можно было установить на устройство.
🚩Неподписанный APK (`unsigned APK`)
- Это черновая версия APK, которая не имеет цифровой подписи.
- Такой APK можно запустить только в эмуляторе или при отладке (debug build).
- Google Play не принимает неподписанные APK.
При сборке debug-версии в Android Studio:
gradlew assembleDebug
Попытка установить неподписанный APK
adb install app-unsigned.apk
Ошибка
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]
🚩Подписанный APK (`signed APK`)
- Подписанный APK содержит цифровую подпись, которая гарантирует, что код не был изменён.
- Android проверяет ключ подписи перед установкой.
Google Play требует подписанный APK или AAB.
Как подписать APK вручную?
apksigner sign --ks my-release-key.jks --out app-signed.apk app-unsigned.apk
🚩Зачем нужна подпись?
Подпись APK гарантирует*
Целостность → код не был изменён после сборки.
Подлинность → приложение подписано разработчиком, а не злоумышленником.
Обновления → только приложения с тем же ключом могут обновлять старую версию.
Ставь 👍 и забирай 📚 Базу знаний | 444 |
| 19 | 🤔 Какие потоки есть в RxJava?
1. Schedulers.io(): для ввода-вывода, работы с сетью или файлами.
2. Schedulers.computation(): для интенсивных вычислений, таких как обработка данных или рендеринг.
3. Schedulers.newThread(): для запуска новых потоков.
4. Schedulers.single(): для выполнения задач в одном потоке.
5. AndroidSchedulers.mainThread(): для работы с главным (UI) потоком на Android.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний | 396 |
| 20 | 🤔 Как в 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 }
}
Ставь 👍 и забирай 📚 Базу знаний | 429 |
