Kotlin | Вопросы собесов
2.57K subscribers
28 photos
966 links
Download Telegram
🤔 Как создать Dagger Hilt?

Для этого необходимо аннотировать класс Application аннотацией
@HiltAndroidApp, а компоненты, в которые требуется внедрение, — @AndroidEntryPoint. Затем зависимости определяются через модули и внедряются автоматически.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Как в 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
🤔 В чём отличие обычного метода от extension метода в Kotlin?

Extension-функция выглядит как будто добавлена в класс, но на самом деле это статическая функция, которой не требуется менять сам класс. Она даёт синтаксический сахар, не добавляя нового в байткод класса.


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

Если удалился элемент из списка, нужно:
Удалить его из списка данных.
Сообщить Adapter, чтобы он перерисовал только изменённые элементы.

🚩Используем `notifyItemRemoved()` (лучший вариант)

Этот метод обновляет только удалённый элемент, сохраняя анимации.
fun removeItem(position: Int) {
itemList.removeAt(position) // Удаляем из списка данных
notifyItemRemoved(position) // Сообщаем адаптеру
notifyItemRangeChanged(position, itemList.size) // Обновляем индексы
}


🚩Если изменился весь список – `DiffUtil`

Если изменился несколько элементов, лучше использовать DiffUtil, чтобы обновить только нужные элементы.
class MyAdapter : ListAdapter<Item, MyViewHolder>(DIFF_CALLBACK) {

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

override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
}
}
}


Обновляем список через submitList()
fun removeItem(item: Item) {
val newList = currentList.toMutableList()
newList.remove(item)
submitList(newList) // `DiffUtil` сам вычислит, что обновить
}


🚩Полное обновление списка (`notifyDataSetChanged()`)

Использовать только если изменился весь список!
fun removeItem(position: Int) {
itemList.removeAt(position)
notifyDataSetChanged() // Перерисовывает весь список (медленно)
}


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

Самый обычный сервис в Android запускается в основном (главном) потоке приложения. Это означает, что для операций, требующих длительного времени, необходимо запускать дополнительные потоки, чтобы не блокировать UI и не вызывать ANR (Application Not Responding). Для фоновых задач лучше использовать IntentService или управлять потоками вручную.

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

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

🚩Почему происходят утечки памяти в Android?

В Android утечки памяти могут возникнуть из-за особенностей работы виртуальной машины (ART или Dalvik), а также из-за того, что сборщик мусора (Garbage Collector, GC) не может освободить память для объектов, на которые по-прежнему существуют ссылки. Это происходит в следующих случаях:

🟠Долгоживущие ссылки
Если объект ссылается на другой объект, который больше не нужен, последний не может быть освобождён GC. Например:
Статические ссылки, которые продолжают "удерживать" объект.
Замыкания (closures), которые хранят ссылки на контекст активности.

🟠Неявные ссылки
Некоторые объекты системы Android, такие как Context, View, Handler, хранят ссылки на компоненты приложения (например, Activity), из-за чего их нельзя освободить.

🟠Неправильное использование API
Ошибки, такие как регистрация слушателей (listeners) без последующей отписки, использование таймеров, которые продолжают работать даже после уничтожения активности, и так далее.

🚩Пример утечки памяти

public class MyActivity extends AppCompatActivity {
private static TextView myTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, Memory Leak!");
}
}


Исправленный код
public class MyActivity extends AppCompatActivity {
private TextView myTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, World!");
}
}


🚩Как избежать утечек памяти?

🟠Избегайте долгоживущих ссылок на объекты активности или контекста
Не используйте static для объектов, ссылающихся на Context или Activity. Используйте WeakReference, если нужно сохранить ссылку, которая не должна блокировать сборщик мусора.

🟠Отключайте слушателей и callback-методы
Если вы регистрируете слушателей (например, через setOnClickListener), обязательно удаляйте их в методах жизненного цикла, например, в onDestroy().

🟠Осторожно используйте анонимные классы и лямбда-выражения
Анонимные классы (например, Runnable, Handler) могут неявно хранить ссылки на внешние классы, что может привести к утечке памяти.

🟠Используйте инструменты для выявления утечек памяти
Android Profiler: встроенный инструмент Android Studio для мониторинга использования памяти. LeakCanary: библиотека, которая автоматически обнаруживает утечки памяти в вашем приложении.

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

- internal — это уровень доступа для всего модуля.
- Модуль — это обычно компиляционная единица: одна сборка Gradle, Maven или IntelliJ.
- Код с internal не будет виден в других модулях, даже если класс или функция — public.
Полезно для сокрытия реализации между слоями или при использовании многомодульной архитектуры.


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

Класс-наследник в 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() // Имя: Бобик, Порода: Лабрадор
}


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

Kotlin не имеет ключевого слова static, но эквиваленты:
- Использовать companion object внутри класса.
- В файле вне классов использовать
@JvmField или const val для констант.
- Или создать object-синглтон.
Это позволяет доступ к значению без создания экземпляра класса.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2
🤔 В каких случаях используют 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` одновременно
}
}


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

- Foreground Service — работает даже в фоне, пока отображается уведомление.
- JobScheduler / WorkManager — выполняют задачи при оптимальных условиях, даже после перезапуска.
- AlarmManager — может запускать код даже после завершения приложения.
- BroadcastReceiver — перехватывает системные события (например, BOOT_COMPLETED).
- Persistence через SharedPreferences, Room — для восстановления состояния.
- Также важно не держать тяжёлую логику в UI и правильно обрабатывать onStop() и onDestroy().


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

Чтобы обрабатывать жесты в Android, используйте класс GestureDetector. Он помогает отслеживать стандартные жесты: одиночные нажатия, свайпы, долгие нажатия, двойные касания и т.д.

🚩Как использовать GestureDetector?

Создайте экземпляр GestureDetector, передав Context и слушателя (GestureDetector.OnGestureListener).
Передавайте события касания в gestureDetector.onTouchEvent(event) из метода onTouchEvent().
class GestureActivity : AppCompatActivity(), GestureDetector.OnGestureListener {
private lateinit var gestureDetector: GestureDetector

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gestureDetector = GestureDetector(this, this)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}

override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val deltaX = e2!!.x - e1!!.x
if (deltaX > 0) Log.d("Gesture", "Swipe Right") else Log.d("Gesture", "Swipe Left")
return true
}

override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) {}
override fun onSingleTapUp(e: MotionEvent?) = false
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent?) {}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Разница PriorityQueue, Deque?

- PriorityQueue — структура, где элементы обрабатываются по приоритету, а не по порядку добавления.
- Deque (двусторонняя очередь) — позволяет добавлять/удалять элементы с обеих сторон. Поддерживает поведение как стека, так и очереди.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 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
👍1💊1
🤔 Что такое рефлексия?

Рефлексия — это механизм, позволяющий программе исследовать и изменять свою структуру (классы, методы, поля) во время выполнения. Она позволяет вызывать приватные методы, создавать экземпляры классов по имени и т.д. Это мощный, но небезопасный и затратный по производительности инструмент.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 В чем разница между работой с 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
🤔 Откуда происходит повторный вызов composable функции?

Повторный вызов происходит из механизма "recomposition". Compose автоматически вызывает функцию снова, если состояние, связанное с этой функцией, изменилось.

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

Сильная ссылка - это ссылка, которая напрямую указывает на объект и предотвращает его сборку сборщиком мусора.

🚩Основные подходы

🟠WeakReference
Использование слабых ссылок (WeakReference) позволяет определить, был ли объект освобожден сборщиком мусора.
🟠ObjectWatcher (в LeakCanary)
LeakCanary использует ObjectWatcher для отслеживания объектов. Если объект не освобожден, ObjectWatcher уведомляет об утечке.

import java.lang.ref.WeakReference;

public class MemoryLeakExample {

public static void main(String[] args) {
// Создание объекта
MyObject myObject = new MyObject();

// Создание слабой ссылки на объект
WeakReference<MyObject> weakRef = new WeakReference<>(myObject);

// Удаление сильной ссылки
myObject = null;

// Вызов сборщика мусора
System.gc();

// Проверка, была ли слабая ссылка освобождена
if (weakRef.get() == null) {
System.out.println("Object has been garbage collected");
} else {
System.out.println("Object is still alive");
}
}

static class MyObject {
// Некоторая логика класса
}
}


🚩Понимание, что объект не используется

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

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

Ты можешь использовать GridLayoutManager и задать количество колонок динамически

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

В Android есть несколько способов навигации между экранами. Давай разберём основные.

🟠Activity-based навигация
Каждый экран – это отдельная Activity, переход осуществляется с помощью Intent.
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)


🟠Fragment-based навигация
Один Activity содержит несколько Fragment, навигация через FragmentManager.
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack(null)
.commit()


🟠Navigation Component (Jetpack)
Google рекомендует Navigation Component для удобной работы с Fragment.
findNavController().navigate(R.id.action_firstFragment_to_secondFragment)


🟠Bottom Navigation / Tab Navigation
Используется BottomNavigationView или TabLayout для переключения между экранами.
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.home -> replaceFragment(HomeFragment())
R.id.profile -> replaceFragment(ProfileFragment())
}
true
}


🟠Drawer Navigation (Navigation Drawer)
Боковое меню (DrawerLayout) с пунктами навигации.
drawerLayout.openDrawer(GravityCompat.START)


🟠Deep Links и App Links
Навигация через ссылки (например, myapp://profile/123).
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="myapp" android:host="profile"/>
</intent-filter>


🟠Navigation в Jetpack Compose
Используется NavHost и NavController.
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile") { ProfileScreen(navController) }
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊1
🤔 Будет ли zip гарантировать параллельность выполнения 2 запросов в сеть, запущенных для Coroutine?

Нет, zip сам по себе не гарантирует параллельность.
Если ты просто напишешь zip(firstCall(), secondCall()), оба вызова начнутся последовательно, и zip просто объединит их результаты.
Чтобы получить параллельность:
- Запусти оба вызова через async.
- Затем передай их await() в zip.


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