Kotlin | Вопросы собесов
2.56K subscribers
27 photos
952 links
Download Telegram
🤔 Как написать наследование между двумя классами в Kotlin?

Через open и :. Родительский класс должен быть open, а дочерний объявляется с class B : A().


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

Являются двумя различными типами коллекций, которые используются для хранения наборов данных. Основные различия между этими двумя структурами данных касаются их внутренней реализации, что влияет на производительность операций добавления, удаления и доступа к элементам.

🚩Внутренняя реализация

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

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

🚩Производительность:

🟠ArrayList
Быстрый доступ к элементам по индексу. Медленные операции добавления и удаления элементов (особенно в начале и середине списка), так как может потребоваться сдвиг оставшейся части массива.

🟠LinkedList
Быстрые операции вставки и удаления элементов, поскольку они требуют только изменения ссылок. Медленный доступ к элементам по индексу, так как для доступа к элементу необходимо пройти по списку.

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

1. Activity переходы:
- Использовать overridePendingTransition() для задания анимации входа и выхода.
2. Fragment переходы:
- Установить FragmentTransaction.setCustomAnimations() или использовать shared element transitions.
3. Navigation Component:
- Указать анимации в nav_graph.xml или через NavOptions.
4. Compose Navigation:
- Использовать AnimatedVisibility, AnimatedContent или другие transition-компоненты.
Анимации переходов делают интерфейс плавным и улучшают UX.


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

Да, потеря состояния (state) может быть связана с фрагментами (Fragments) в Android. Это довольно распространённая проблема, особенно при работе с динамическими интерфейсами. Давайте разберёмся, почему она возникает, как её предотвратить и какие решения существуют.

🚩Почему происходит потеря состояния во фрагментах?

🟠Жизненный цикл фрагмента
Фрагменты имеют сложный жизненный цикл, который тесно связан с активностью. Основные этапы:
onCreate() — создаётся фрагмент, инициализируются объекты.
onViewCreated() — создаётся View (UI компоненты).
onStart() / onResume() — фрагмент становится видимым и активным.
onPause() / onStop() — фрагмент приостанавливается.
onDestroyView() — уничтожается только View (UI), но сам фрагмент всё ещё существует.
onDestroy() — полностью уничтожается фрагмент.
Фрагменты могут пересоздаваться системой, например, при смене ориентации экрана или нехватке памяти. Если разработчик неправильно сохраняет состояние фрагмента, оно может быть утеряно.

🟠Удаление View фрагмента системой
Когда фрагмент переходит в состояние onDestroyView(), его View уничтожается, но сам объект фрагмента сохраняется. Если пользователь вернётся к этому фрагменту, View будет пересоздана, и вы потеряете все изменения, сделанные ранее, если они не сохранены явно.

🟠Проблемы с менеджером фрагментов
Использование FragmentManager или FragmentTransaction с неправильными методами, такими как replace() или add(), без должного управления стэком (back stack), может привести к пересозданию или дублированию фрагментов, что вызывает потерю состояния.

🟠Проблемы с `savedInstanceState`
Фрагменты, как и активности, используют механизм сохранения состояния через BundleonSaveInstanceState). Если состояние не сохранено правильно, данные могут быть потеряны.

🚩Как предотвратить потерю состояния во фрагментах?

🟠Сохранение данных в `onSaveInstanceState`
При уничтожении фрагмента система вызывает метод onSaveInstanceState(). Здесь вы можете сохранить важные данные в Bundle.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key_text", editText.text.toString())
}


При пересоздании фрагмента данные можно восстановить в onViewCreated()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val restoredText = savedInstanceState?.getString("key_text")
editText.setText(restoredText)
}


🟠Использование `ViewModel`
Для хранения состояния, которое переживает уничтожение и пересоздание фрагмента, лучше использовать архитектурный компонент ViewModel.
1⃣Создайте ViewModel
class MyViewModel : ViewModel() {
val textData = MutableLiveData<String>()
}


2⃣Используйте его во фрагменте
class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

// Восстановление данных
viewModel.textData.observe(viewLifecycleOwner) { text ->
editText.setText(text)
}

// Сохранение данных при изменении
editText.addTextChangedListener {
viewModel.textData.value = it.toString()
}
}
}


🟠Использование `FragmentManager` правильно
При работе с фрагментами всегда добавляйте их в back stack, если вы хотите сохранить состояние.
val fragment = MyFragment()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit()


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

Android оценивает приоритет процесса:
1. Foreground (не трогается),
2. Visible,
3. Service,
4. Cached (в фоне),
5. Empty (не содержит компонентов).
Убиваются сначала самые низкоприоритетные, при нехватке памяти.


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

Методы get и replace в Kotlin относятся к работе с коллекциями, карты (Map) или к строкам. В зависимости от контекста, они вызываются через разные классы. Давайте разберем каждый случай отдельно.

🟠Методы `get` и `replace` для коллекций (`Map`)
В контексте работы с картами (Map), методы get и replace относятся к получению значений по ключу и замене существующих значений.
Метод get
Метод get используется для извлечения значения из карты по указанному ключу.
val map = mapOf("key1" to "value1", "key2" to "value2")
println(map.get("key1")) // value1
println(map["key2"]) // value2 (альтернатива `get`)


Метод replace используется для обновления значения, связанного с определённым ключом, если он существует. Этот метод доступен только для изменяемых карт (MutableMap).
val mutableMap = mutableMapOf("key1" to "value1", "key2" to "value2")
mutableMap.replace("key1", "newValue1")
println(mutableMap) // {key1=newValue1, key2=value2}


🟠Методы `get` и `replace` для строк
В контексте строк, методы get и replace работают с символами и подстроками.
Метод get используется для доступа к символу строки по индексу. Это альтернатива квадратным скобкам.
val text = "Kotlin"
println(text.get(0)) // K
println(text[1]) // o (альтернатива `get`)


Метод replace заменяет символы или подстроки в строке на заданные.
val text = "Kotlin is awesome"
val newText = text.replace("awesome", "powerful")
println(newText) // Kotlin is powerful


🟠Глобальные методы `get` и `replace` через пользовательские классы
Если вы пишете свои классы, вы можете переопределить оператор get и метод replace, чтобы использовать их для своих нужд.
class CustomList<T>(private val items: List<T>) {
operator fun get(index: Int): T {
return items[index]
}
}

fun main() {
val customList = CustomList(listOf(1, 2, 3))
println(customList[0]) // 1
}


Пример с replace
class CustomMap<K, V>(private val map: MutableMap<K, V>) {
fun replace(key: K, value: V) {
if (map.containsKey(key)) {
map[key] = value
}
}
}

fun main() {
val customMap = CustomMap(mutableMapOf("key1" to "value1"))
customMap.replace("key1", "newValue1")
println(customMap) // {key1=newValue1}
}


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

В Kotlin абстрактные классы и методы по умолчанию open, что позволяет их переопределять без явного указания модификатора open. В Java абстрактные методы всегда подразумевают переопределение, а обычные методы должны быть явно помечены abstract или final.

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

🚩Вставка в `ArrayList`

ArrayList реализован на основе массива.

🟠Вставка в конец
Амортизированное O(1). Если массив не заполнен, элемент добавляется в конец списка. В случае, если массив заполнен, требуется выделение нового массива и копирование элементов, что занимает O(n) времени.
val arrayList = ArrayList<Int>()
arrayList.add(1) // Быстрая вставка в конец


🟠Вставка в начало или середину
O(n). Необходимо сдвинуть все элементы, начиная с позиции вставки, чтобы освободить место для нового элемента. Это требует времени, пропорционального количеству элементов после позиции вставки.
arrayList.add(0, 2) // Медленная вставка в начало
arrayList.add(1, 3) // Медленная вставка в середину


🚩Вставка в LinkedList

LinkedList реализован на основе узлов, где каждый узел содержит ссылку на следующий и/или предыдущий узел (в случае двусвязного списка).

🟠Вставка в начало: O(1). Для добавления элемента в начало достаточно изменить ссылки первого узла и нового узла.
val linkedList = LinkedList<Int>()
linkedList.addFirst(1) // Быстрая вставка в начало


🟠Вставка в конец
O(1) (если есть ссылка на последний узел) или O(n) (если необходимо пройти весь список). Если список двусвязный и хранится ссылка на последний элемент, вставка в конец осуществляется за O(1). В противном случае, требуется пройти весь список до конца.
linkedList.addLast(2) // Быстрая вставка в конец, если есть ссылка на последний узел


🟠Вставка в середину
O(n). Необходимо пройти список до нужной позиции и изменить ссылки узлов.
linkedList.add(1, 3) // Медленная вставка в середину


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

DEX — Dalvik Executable — бинарный формат байткода, исполняемый Dalvik/ART.
Файл classes.dex содержит весь скомпилированный Kotlin/Java код.


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

Модификаторы доступа (Access Modifiers) — это ключевые слова, которые определяют, кто может видеть и использовать класс, переменную или метод.
Они помогают инкапсулировать данные и защищать код от неправильного использования.

🚩Модификаторы доступа в Java

Пример Java
public class Example {
private int a = 10; // Только внутри класса
int b = 20; // Видно внутри пакета (package-private)
protected int c = 30; // Видно в пакете и наследниках
public int d = 40; // Доступно везде
}


🚩Модификаторы доступа в Kotlin

В Kotlin есть почти такие же модификаторы, но package-private заменён на internal.
Пример Kotlin
class Example {
private val a = 10 // Только в этом классе
internal val b = 20 // Видно в модуле
protected val c = 30 // Видно в наследниках
public val d = 40 // Видно везде (по умолчанию)
}


🚩Где используются модификаторы доступа?

Для полей (переменных класса)
public class User {
private String name; // Скрыто от других классов

public User(String name) {
this.name = name;
}

public String getName() { // Доступ через метод
return name;
}
}


Для методов
class Animal {
protected void makeSound() { // Доступен только в наследниках
System.out.println("Животное издаёт звук");
}
}
class Dog extends Animal {
public void bark() {
makeSound(); // Разрешено, потому что `protected`
System.out.println("Гав-гав!");
}
}


Для классов
public class Car { } //  Доступен везде
class Engine { } // Только в этом пакете


В Kotlin можно делать private class, но только внутри другого класса.
class Car {
private class Engine // Только в этом классе
}


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

Executor — это абстракция для управления потоками. Он позволяет отправлять задачи на выполнение, не заботясь напрямую о создании и управлении потоками.


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

Если профайлер показывает, что рендеринг какого-либо фрейма занял 120 миллисекунд, это означает, что этот фрейм выполнялся слишком долго, что приводит к фризам и лагам в пользовательском интерфейсе.

🚩Разбор проблемы

🟠Что значит "фрейм" в этом контексте?
В Android интерфейс обновляется 60 раз в секунду (частота 60 FPS). Это значит, что каждый кадр (фрейм) должен рендериться не дольше 16,67 мс (1000 мс / 60 FPS).

🟠Почему 120 мс — это плохо?
Если рендеринг кадра занимает 120 мс, то за это время устройство должно было бы нарисовать 7 кадров (120 / 16,67 ≈ 7). Однако оно успело обработать только один, что приводит к заметному подтормаживанию.

🟠Что может вызывать такие задержки?
Тяжёлые вычисления в основном потоке (UI Thread) – например, сложные математические операции, работа с JSON, парсинг файлов.
Долгие операции с рендерингом – сложные векторные изображения, перегруженные Canvas.draw() или анимации.
Синхронные вызовы I/O (чтение файлов, базы данных, сети) – если, например, в onDraw() идёт обращение к диску или базе данных.
Неоптимальный layout – глубокая иерархия ViewGroup, частые перерасчёты макетов (measure/layout).

🚩Как исправить?

🟠Перенести тяжёлые вычисления в фоновый поток
Coroutines, Executors, WorkManager
🟠Использовать профайлер Android Studio
для поиска "узких мест".
🟠Оптимизировать рендеринг
избегать сложных onDraw(), использовать ViewStub, RecyclerView.
🟠Пересмотреть макет
убрать ненужные ViewGroup, использовать ConstraintLayout.

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

- HashSet — не гарантирует порядок элементов, основан на хеш-таблице.
- LinkedHashSet — сохраняет порядок добавления элементов.
- TreeSet — отсортированное множество (по натуральному порядку или Comparator), основано на красно-чёрном дереве.


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

В 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
👍1
🤔 Чем отличается data push от identification push?

– Data push — содержит полезную нагрузку (данные), которые обрабатываются приложением (например, текст сообщения, обновления).
– Identification push — содержит только метаинформацию, которая говорит приложению, что нужно самостоятельно запросить данные с сервера.


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

В Kotlin все классы неявно наследуются от Any (аналог Object в Java).
Это значит, что любой класс в Kotlin имеет 3 базовых метода:

🚩`equals()` – сравнение объектов

По умолчанию сравнивает ссылки (как ===)
class Person(val name: String)

fun main() {
val p1 = Person("Alice")
val p2 = Person("Alice")

println(p1 == p2) // false (разные объекты)
}


Как переопределить equals() для сравнения по значениям?
class Person(val name: String) {
override fun equals(other: Any?): Boolean {
return other is Person && this.name == other.name
}
}

fun main() {
val p1 = Person("Alice")
val p2 = Person("Alice")

println(p1 == p2) // true (теперь сравниваются значения)
}


🚩`hashCode()` – хеш-код объекта

По умолчанию генерируется на основе ссылки
val p = Person("Alice")
println(p.hashCode()) // Разный для каждого объекта


Как переопределить hashCode()?
class Person(val name: String) {
override fun hashCode(): Int {
return name.hashCode() // Генерируем хеш-код на основе имени
}
}


🚩`toString()` – строковое представление объекта

По умолчанию печатает ClassName@hashCode
val p = Person("Alice")
println(p.toString()) // Person@4e25154f (неудобный вывод)


Как сделать красивый вывод?
class Person(val name: String) {
override fun toString(): String {
return "Person(name=$name)"
}
}

val p = Person("Alice")
println(p.toString()) // Person(name=Alice)


🚩Data-классы автоматически переопределяют `equals()`, `hashCode()`, `toString()`

data class User(val name: String)

fun main() {
val u1 = User("Alice")
val u2 = User("Alice")

println(u1 == u2) // true (по значениям)
println(u1.hashCode()) // Одинаковый для объектов с одинаковыми данными
println(u1.toString()) // User(name=Alice)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🤔1
🤔 Что такое px, dp, sp?

- px (pixels) — абсолютные пиксели экрана.
- dp (density-independent pixels) — масштабируемые пиксели, зависят от плотности экрана.
- sp (scale-independent pixels) — как dp, но учитывают пользовательские настройки размера шрифта.


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

В Java класс Object является корневым классом для всех остальных классов. Это значит, что все классы в Java неявно наследуются от Object, если явно не указано другое.

🚩Основные методы класса `Object`

🟠`equals(Object obj)`
Определяет, равны ли два объекта. По умолчанию использует сравнение по ссылке (==), но может быть переопределён для логического сравнения.
     class Person {
String name;

Person(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}

public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
}
}


🟠`hashCode()`
Возвращает числовой хеш-код объекта, используемый, например, в HashMap. Если переопределяем equals(), нужно переопределить и hashCode().
     @Override
public int hashCode() {
return Objects.hash(name);
}


🟠`toString()`
Возвращает строковое представление объекта. По умолчанию – имя класса + хеш-код, но лучше переопределять.
     @Override
public String toString() {
return "Person{name='" + name + "'}";
}


🟠`getClass()`
Возвращает объект Class, описывающий класс объекта.
     System.out.println(p1.getClass().getName()); // Person


🟠`clone()`
Создаёт копию объекта (поверхностное клонирование). Объект должен реализовать Cloneable, иначе будет CloneNotSupportedException.
     class Person implements Cloneable {
String name;

Person(String name) {
this.name = name;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


🟠`finalize()` *(устарел, не рекомендуется использовать)*
Вызывается перед удалением объекта сборщиком мусора. Лучше использовать try-with-resources и close().
     @Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}


🟠`wait()`, `notify()`, `notifyAll()`
Методы для работы с многопоточностью. wait() – приостанавливает поток до вызова notify(). notify() – пробуждает один поток. notifyAll() – пробуждает все потоки.
     class SharedResource {
synchronized void doWait() throws InterruptedException {
wait();
}

synchronized void doNotify() {
notify();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как вызвать метод Activity из фрагмента?

Во фрагменте можно получить доступ к активности через requireActivity() или activity, и вызвать у неё метод, предварительно приведя к нужному типу (например, activity as MyActivity). Лучше всего — через интерфейс, реализуемый активностью, для слабой связанности.


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

По умолчанию нельзя, потому что дженерики стираются (Type Erasure) во время компиляции.
fun <T> printType(value: T) {
println(T::class.simpleName) // Ошибка! Нельзя использовать `T::class`
}


🚩Как получить тип дженерика? (`reified`)

Если используем inline fun, можно сделать дженерик "реальным" (reified).
inline fun <reified T> printType(value: T) {
println(T::class.simpleName) // Работает!
}

fun main() {
printType(123) // Int
printType("Hello") // String
printType(3.14) // Double
}


🚩Как получить тип дженерика в классе? (`KType`)

Если дженерик используется в классе, то reified не поможет.
class MyGenericClass<T>(private val type: KClass<T>) {
fun printType() {
println(type.simpleName) // Работает
}
}

fun main() {
val obj = MyGenericClass(String::class)
obj.printType() // String
}


🚩Как получить тип списка (`List<T>`) в `runtime`?

Для сложных дженериков (List<T>, Map<K, V>) используем typeOf<T>() (только с reified).
import kotlin.reflect.typeOf

inline fun <reified T> printGenericType() {
val type = typeOf<T>()
println(type) // List<Int>, Map<String, Boolean> и т. д.
}

fun main() {
printGenericType<List<Int>>() // kotlin.collections.List<kotlin.Int>
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое object / companion object?

`object` в Kotlin используется для создания синглтона — объекта, экземпляр которого существует в одном экземпляре. `companion object` позволяет создать статические члены класса, доступные без создания экземпляра класса, аналогично статическим членам в Java.

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