Kotlin | Вопросы собесов
2.56K subscribers
28 photos
972 links
Download Telegram
🤔 Что такое лямбды с точки зрения синтаксиса в Java и Kotlin?

В Java лямбды — это упрощённый синтаксис для анонимных классов, реализующих функциональный интерфейс. Синтаксис: (параметры) -> { тело }. В Kotlin лямбды представляют собой выражения, передаваемые как функции, с синтаксисом { параметры -> тело }. Kotlin более лаконичен, позволяя опускать параметры, если их можно вывести из контекста.

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

Да, Android Framework активно использует паттерн Factory в различных API. Один из самых известных примеров — LayoutInflater.

🚩Пример: `LayoutInflater` (Фабричный метод)

В Android для создания (инстанцирования) UI-компонентов из XML используется LayoutInflater, который реализует паттерн Factory Method. Вместо того чтобы вручную создавать объекты View, система предоставляет фабричный метод inflate(), который "производит" экземпляры View.

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

В XML описан интерфейс.
LayoutInflater загружает XML и создает соответствующие объекты View.
Это абстрагирует создание UI-компонентов от разработчика.
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.custom_layout, parent, false)


🚩Другие примеры использования Factory в Android

🟠`MediaPlayer.create()`
Вместо MediaPlayer() напрямую используется MediaPlayer.create(context, R.raw.sound), который автоматически создаёт и настраивает объект.

🟠`PreferenceManager.getDefaultSharedPreferences()`
Позволяет получить SharedPreferences без необходимости вручную создавать экземпляр.

🟠`Fragment.instantiate()`
Фабричный метод для создания Fragment без явного вызова конструктора.

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

MV* паттерны — это архитектурные подходы, разделяющие логику приложения на слои:
- MVC (Model-View-Controller): Контроллер управляет моделью и обновляет представление.
- MVP (Model-View-Presenter): Презентер принимает действия от view, взаимодействует с моделью и обновляет UI.
- MVVM (Model-View-ViewModel): ViewModel содержит логику и состояние, а view автоматически подписана на изменения через биндинг.
Цель — отделить UI от логики и улучшить масштабируемость, читаемость и тестируемость.


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

ViewModel и onSaveInstanceState служат для сохранения данных при изменении конфигурации активности или фрагмента (например, при повороте экрана). Однако они решают эту задачу по-разному и имеют разные области применения.

🟠ViewModel
ViewModel используется для хранения и управления данными, связанных с UI, таким образом, чтобы они сохранялись при изменении конфигурации (например, при повороте экрана).

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

Когда система уничтожает и пересоздаёт Activity или Fragment, ViewModel остаётся в памяти до полного уничтожения владельца (например, выхода из Activity).
class MyViewModel : ViewModel() {
var counter: Int = 0 // Переменная, которая сохраняется при повороте экрана
}


Использование в Activity
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

// Теперь viewModel.counter сохранится при повороте экрана
}
}



🚩Плюсы

Хранит данные в памяти
до полного уничтожения Activity или Fragment.
Удобно для хранения сложных объектов
списки, модели, API-данные
Позволяет разделять логику и UI
улучшая архитектуру.

🚩Минусы

Данные исчезают при уничтожении Activity
например, при закрытии приложения
Не сохраняет данные при завершении процесса например, при нехватке памяти

🚩onSaveInstanceState()

Метод onSaveInstanceState() используется для сохранения данных в Bundle, который система автоматически передаёт при пересоздании Activity или Fragment.

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

Когда Activity уничтожается (например, при повороте экрана), система вызывает onSaveInstanceState(), в котором можно сохранить небольшие данные (строки, числа и т. д.). После пересоздания Activity эти данные можно восстановить из savedInstanceState.
class MainActivity : AppCompatActivity() {
private var counter: Int = 0 // Значение, которое мы хотим сохранить

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// Восстанавливаем данные, если они есть
counter = savedInstanceState?.getInt("counter_key") ?: 0
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("counter_key", counter) // Сохраняем значение перед уничтожением Activity
}
}


🚩Плюсы

Сохраняет данные даже при завершении процесса (например, при нехватке памяти).
Позволяет быстро восстановить состояние UI (например, текст в EditText).

🚩Минусы

Подходит только для небольших данных (числа, строки).
Не подходит для хранения сложных объектов (списки, большие модели, API-данные).
Требует кодирования и декодирования данных в Bundle

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

Производительность измеряется в миллисекундах времени отрисовки (measure, layout, draw). Также учитывается количество операций пересчёта и перекомпоновки. В Jetpack Compose оценивается количеством recomposition и snapshot изменений.


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


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

Когда выполнение suspend-функции приостанавливается (например, при delay или await), состояние функции сохраняется в continuation-объекте, а текущий поток освобождается.
Позже выполнение возобновляется с этого же места — как будто ничего не происходило. Это реализовано через стейт-машину и трансформацию кода компилятором Kotlin.


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

sealed class – это ограниченная иерархия классов, где можно создавать разные подклассы с разными свойствами.
enum class – это фиксированный набор однотипных объектов, которые не имеют разной структуры.

🚩`enum class` – для фиксированного набора значений

Когда значения не изменятся (например, дни недели, цвета, статусы).
Когда у всех значений одинаковая структура.
enum class Status {
LOADING, SUCCESS, ERROR
}


Можно добавлять свойства и методы
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF");

fun printHex() = println(hex)
}

fun main() {
val color = Color.RED
println(color.hex) // #FF0000
color.printHex() // #FF0000
}


🚩`sealed class` – для сложных состояний с разной структурой

Когда у состояний разные параметры и поведение.
Когда нужен when, который проверяет все возможные подклассы.
sealed class Status {
object Loading : Status()
data class Success(val data: String) : Status()
data class Error(val message: String) : Status()
}


Использование с when (без else)
fun handleStatus(status: Status) {
when (status) {
is Status.Loading -> println("Загрузка...")
is Status.Success -> println("Данные: ${status.data}")
is Status.Error -> println("Ошибка: ${status.message}")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 В чём разница между let, run, also, apply?

- let — выполняет блок с результатом и возвращает последний оператор.
- run — используется для вычислений, возвращает результат.
- also — возвращает исходный объект и используется для побочных действий.
- apply — настраивает объект и возвращает его.


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

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 гарантирует*
Целостность → код не был изменён после сборки.
Подлинность → приложение подписано разработчиком, а не злоумышленником.
Обновления → только приложения с тем же ключом могут обновлять старую версию.

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

Данные передаются с помощью специального контейнера Data, который прикрепляется к задаче при её создании. Внутри Worker эти данные можно получить через inputData, а также можно вернуть результат выполнения обратно, используя outputData, который затем доступен через LiveData или статус задачи.


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

При наследовании data class от какого-либо суперкласса в Kotlin, важно понимать, как работают свойства (поля) суперкласса и как они влияют на функциональность и структуру data class.

🚩Что происходит с полями суперкласса

🟠Наследование свойств
Поля (свойства), объявленные в суперклассе, автоматически становятся доступными в классе-наследнике. Вы можете использовать их в наследуемом классе как обычно. Однако свойства суперкласса не участвуют в автоматически сгенерированных функциях equals(), hashCode(), и toString() для data class.

🟠Автоматически сгенерированные функции в data-классе
У data class Kotlin генерирует функции equals(), hashCode(), toString(), copy() и другие. Эти функции работают только с параметрами, объявленными в первичном конструкторе data-класса. Поля, которые находятся в суперклассе, не участвуют в этих функциях.

🟠Почему поля суперкласса игнорируются
Это связано с тем, что контракт data class предполагает, что все его ключевые данные (data) определяются только параметрами первичного конструктора. Это позволяет гарантировать, что две одинаковые сущности будут сравниваться и обрабатываться корректно, основываясь только на данных самого data class.

// Суперкласс с полем name
open class Person(val name: String)

// Наследуемый data-класс
data class Employee(val id: Int, val position: String) : Person(name = "Default")


val employee1 = Employee(1, "Developer")
val employee2 = Employee(1, "Developer")

println(employee1 == employee2) // true, так как сравнение основано только на id и position
println(employee1.toString()) // Employee(id=1, position=Developer)


🟠Если нужно включить поля суперкласса в сравнение
Если вы хотите, чтобы поля суперкласса учитывались в логике equals() или hashCode(), вам нужно переопределить эти функции вручную.
data class Employee(val id: Int, val position: String) : Person(name = "Default") {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Employee || !super.equals(other)) return false
return id == other.id && position == other.position && name == other.name
}

override fun hashCode(): Int {
return 31 * super.hashCode() + id.hashCode() + position.hashCode()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 На что обратить внимание при работе с фрагментами?

- Жизненный цикл — особенно при навигации и конфигурации.
- Утечки памяти через context/view.
- Передача аргументов через Bundle, а не напрямую.
- Подписка на данные — отвязка в onDestroyView.
- Навигация — через Navigation Component или FragmentManager.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1
Forwarded from easyoffer
🎉 easyoffer 2.0 — релиз уже в этом месяце!

Вас ждут новые фичи, о которых мы ранее даже не упоминали. Они сделают путь к офферам ещё быстрее и эффективнее. Расскажу о них чуть позже 👀

В честь запуска мы готовим ограниченную акцию:

Первые 500 покупателей получат:
🚀 PRO тариф на 1 год с 50% скидкой

Что нужно сделать:

🔔 Подпишитесь на этот Telegram-канал, чтобы первыми узнать о старте релиза. Сообщение появится в нем раньше, чем где-либо еще — вы успеете попасть в число первых 500 и получить максимальную выгоду. 🎁 А еще только для подписчиков канала ценный бонус в подарок к PRO тарифу.

📅 Официальный запуск — уже совсем скоро.
Следите за новостями и не пропустите старт!
🤔 Зачем нужно разделять отображение и бизнес-логику?

Разделение UI и бизнес-логики делает код понятнее, тестируемее и проще в поддержке.
Это ключевой принцип чистой архитектуры (Clean Architecture) и паттернов MVP, MVVM, MVI.

🚩Что такое UI и бизнес-логика?

Плохой код (UI + логика в Activity)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
val result = fetchData() // Логика в UI
button.text = result
}
}

fun fetchData(): String {
return "Данные с сервера" // Тут должна быть ViewModel
}
}


🚩Как правильно разделять UI и логику? (MVVM)

Разделим код на Activity + ViewModel
MainActivity (только UI)
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val button = findViewById<Button>(R.id.button)

viewModel.text.observe(this) { text ->
button.text = text // UI обновляется из ViewModel
}

button.setOnClickListener {
viewModel.loadData() // Вызываем бизнес-логику
}
}
}


MainViewModel (бизнес-логика)
class MainViewModel : ViewModel() {
private val _text = MutableLiveData<String>()
val text: LiveData<String> = _text

fun loadData() {
_text.value = "Данные с сервера" // UI не знает, откуда данные
}
}


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

1. Try-catch внутри launch {} или async {} – локальная обработка ошибок.
2. CoroutineExceptionHandler – глобальная обработка в launch, но не работает с async.
3. supervisorScope {} – дочерние корутины не завершают родительский scope при ошибке.
4. try-catch вокруг await() – обработка исключений из async.


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

Проблемы с элементами списка в Android-приложениях могут быть разнообразными. Давайте рассмотрим некоторые из наиболее распространённых проблем и способы их решения.

🚩 Переполнение памяти (Out of Memory)

🟠Почему это происходит: Списки с большим количеством элементов, особенно если элементы содержат изображения или другие ресурсоемкие данные, могут вызывать переполнение памяти.
🟠Решение: Использовать RecyclerView вместо ListView. RecyclerView более эффективно управляет памятью за счет повторного использования представлений. Кроме того, стоит использовать библиотеки для загрузки изображений, такие как Glide или Picasso, которые обеспечивают кэширование и оптимизацию памяти
// Пример использования Glide для загрузки изображений
Glide.with(context)
.load(imageUrl)
.into(imageView)


🚩Медленная прокрутка (Lagging)

🟠Почему это происходит: Прокрутка может быть медленной, если элементы списка отрисовываются или загружаются слишком долго.
🟠Решение: Оптимизировать адаптер списка. Использовать ViewHolder паттерн, чтобы избежать ненужных вызовов findViewById. Также стоит выполнять тяжелые операции, такие как загрузка изображений, асинхронно.
   class MyAdapter(private val itemList: List<Item>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return ViewHolder(view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = itemList[position]
holder.textView.text = item.text
Glide.with(holder.itemView.context).load(item.imageUrl).into(holder.imageView)
}

override fun getItemCount() = itemList.size
}


🚩Неправильное отображение данных (Data Inconsistency)

🟠Почему это происходит: Если адаптер списка неправильно управляет обновлением данных, элементы могут отображать неправильные или устаревшие данные.
🟠Решение: Обеспечить корректное обновление данных в адаптере и использовать DiffUtil для вычисления изменений в данных и обновления только необходимых элементов.
class ItemDiffCallback : 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
}
}

// Использование в адаптере
val diffCallback = ItemDiffCallback()
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(myAdapter)


🚩Многопоточность (Concurrency Issues)

🟠Почему это происходит: Если обновление данных списка происходит из разных потоков без должной синхронизации, это может привести к ошибкам.
🟠Решение: Обеспечить синхронизацию обновлений данных. Использовать подходы, такие как LiveData или Flow из библиотеки Jetpack, чтобы обновления происходили в главном потоке.
val liveData = MutableLiveData<List<Item>>()

liveData.observe(viewLifecycleOwner, Observer { items ->
myAdapter.submitList(items)
})


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

JIT (Just-In-Time compilation) — технология, при которой байткод компилируется в машинный код прямо во время выполнения.
Плюсы:
- Повышение производительности на часто вызываемом коде.
- Гибкая оптимизация под конкретное устройство.
Используется в Java, Dalvik и некоторых JavaScript-движках.


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

🟠Null Safety (Безопасность null)
Переменные по умолчанию не могут быть null, что предотвращает NullPointerException.
var a: String = "abc"
var b: String? = "abc"
b = null // Допустимо


🟠Коллекции (Collections)
Разделение на изменяемые и неизменяемые коллекции.
val list: List<String> = listOf("a", "b", "c")  // Неизменяемый список
val mutableList: MutableList<String> = mutableListOf("a", "b", "c") // Изменяемый список


🟠Data Classes (Классы данных)
Автоматическое создание методов equals(), hashCode(), и toString().
data class User(val name: String, val age: Int)   


🟠Smart Casts (Умные приведения типов)
Автоматическое приведение типа после проверки с помощью is.
fun demo(x: Any) {
if (x is String) {
println(x.length)
}
}


🟠Sealed Classes (Запечатанные классы)
Упрощают обработку ограниченных иерархий классов.
sealed class Expr
data class Const(val number: Double) : Expr()


🟠Выведение типов (Type Inference)
Kotlin автоматически определяет тип переменной.
val x = 10  // Int


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🤔1
🤔 Зачем нужен контекст и каковы его функции?

Контекст (Context) предоставляет доступ к системным ресурсам и функциям:
- доступ к Resources, Assets,
- запуск Activity, Service,
- доступ к SharedPreferences,
- получение SystemService.
Это один из ключевых элементов Android, передающий «среду выполнения».


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