Kotlin | Вопросы собесов
2.56K subscribers
30 photos
970 links
Download Telegram
🤔 Зачем нужен scope в Dagger?

Scope управляет временем жизни объектов, создаваемых Dagger. Он определяет, сколько времени объект будет существовать в зависимости от компонента, к которому он привязан. Это позволяет:
1. Контролировать память: избегать лишнего создания объектов.
2. Гарантировать единственный экземпляр: например,
@Singleton обеспечивает создание объекта один раз на компонент.
3. Разделять области ответственности: создавать зависимости, привязанные к жизненному циклу Activity, Fragment, ViewModel и т.д.


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

Бранчинг (ветвление) — это способ управления кодом в Git, когда разработчики работают в отдельных ветках (branches).
Основные стратегии бранчинга
Git Flow
GitHub Flow
GitLab Flow
Trunk-Based Development

🚩Git Flow – классическая модель с `develop` и `release`

Основные ветки:
main (стабильная версия, релизы).
develop (основная ветка разработки).
Временные ветки:
feature/* (новые фичи, мерджатся в develop).
release/* (готовится релиз, тестирование, фикс багов).
hotfix/* (критические фиксы в main).
Схема Git Flow:
main ──── hotfix ─▶️ merge ────▶️ main

├── develop ─▶️ release ─▶️ merge ─▶️ main
│ │
├── feature/1
├── feature/2


🚩GitHub Flow – упрощённый процесс для CI/CD

Только две основные ветки:
main (всегда стабильная версия).
Фичи разрабатываются в feature/* и сразу мерджатся в main.
Деплой возможен сразу после мерджа в main.
Схема GitHub Flow
main ────▶️ feature/1 ─▶️ merge ─▶️ main ─▶️ deploy
└── feature/2 ─▶️ merge ─▶️ main ─▶️ deploy


🚩GitLab Flow – баланс между Git Flow и GitHub Flow

main – стабильная ветка (готовая к продакшену).
develop (опционально) – если нужно тестирование перед main.
feature/* – для разработки новых фич.
production, staging – если нужно разделение сред.
hotfix/* – фиксы продакшена.
main ────▶️ production

├── staging ───▶️ merge ─▶️ main

├── feature/1 ─▶️ merge ─▶️ staging
├── feature/2 ─▶️ merge ─▶️ staging


🚩Trunk-Based Development – одна ветка (`main`)

Разработчики работают прямо в main, без feature/* веток.
- Коммиты в main маленькие и частые.
- Используются Feature Flags (фичи включаются/выключаются динамически).
Схема Trunk-Based
main ────▶️ commit ─▶️ commit ─▶️ commit ─▶️ deploy


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

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

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

Самый обычный сервис, который наследуется от класса Service, по умолчанию запускается в главном потоке приложения, который также называется UI-потоком (User Interface Thread). Это означает, что все операции, выполняемые в сервисе, включая методы onStartCommand(), onCreate(), и onBind(), выполняются в главном потоке. Если в сервисе будут выполняться длительные или ресурсоемкие операции, такие как сетевые запросы, обработка больших данных или выполнение сложных вычислений, это может привести к "зависанию" пользовательского интерфейса и появлению сообщений о том, что приложение не отвечает (ANR — Application Not Responding).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1💊1
🤔 Разница между throw и throws?

- throw используется для генерации исключения в конкретном месте кода.
- throws указывается в сигнатуре метода, и сообщает, что метод может выбросить исключение.


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


🚩Плюсы

Улучшение читаемости и организации кода
Расширения позволяют держать методы, связанные с определёнными типами данных, ближе к использованию этих типов.

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

Реиспользование кода
Методы, добавленные через расширения, могут быть легко переиспользованы в различных частях приложения.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 AAB (Android App Bundle) — в чём разница?

- APK — готовый исполняемый архив для конкретного устройства.
- AAB — содержит все ресурсы и DEX-коды для всех конфигураций, но не устанавливается напрямую.
Google Play генерирует APK под конкретное устройство, уменьшая размер загрузки.
Формат обязателен для публикации в Google Play с 2021 года.


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

Передача больших данных (например, изображений, видео, JSON) между Activity требует оптимального подхода, потому что:
Intent.putExtra() имеет ограничение по размеру (~1MB).
Передача Bitmap в Intent может вызвать TransactionTooLargeException.
Большие данные лучше передавать через Uri, БД или FileProvider.

Неправильный способ (НЕ ДЕЛАТЬ!) – Bitmap через Intent
val bitmap: Bitmap = getBitmap()
val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // ОПАСНО! Может вызвать Exception
startActivity(intent)


🚩Лучший способ – передача через `Uri` (`FileProvider`)

Сохраняем изображение во File и получаем Uri
fun saveBitmapToFile(context: Context, bitmap: Bitmap): Uri {
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}
return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
}


Передаём Uri через Intent
val uri = saveBitmapToFile(this, bitmap)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Даем доступ другому Activity
}
startActivity(intent)


Получаем Uri в ImageActivity и загружаем изображение
val uriString = intent.getStringExtra("image_uri")
val uri = Uri.parse(uriString)

val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
imageView.setImageBitmap(bitmap)


🚩Альтернативный способ – через Базу Данных (`Room`)

Если изображение уже хранится в базе данных (Room), передаём ID записи, а не сам файл.
val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image_id", imageId) // Передаём только ID
startActivity(intent)


🚩Альтернативный способ – через `SharedPreferences` (только путь к файлу!)
Сохраняем путь
val filePath = saveBitmapToFile(this, bitmap).toString()
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_image", filePath)
.apply()


Читаем путь в Activity
val filePath = getSharedPreferences("app_prefs", MODE_PRIVATE)
.getString("last_image", null)
val bitmap = BitmapFactory.decodeFile(filePath)
imageView.setImageBitmap(bitmap)


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

Формат WebP является наиболее экономичным с точки зрения сжатия без потери качества. Он лучше по сжатию, чем PNG или JPEG, поддерживает прозрачность и используется во многих Android-приложениях.


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

🟠Profile GPU Rendering
Этот инструмент показывает время, затраченное на отрисовку каждого кадра. Использование этого инструмента позволяет выявить "тяжелые" кадры и измерить улучшения после оптимизации.

1⃣Включите режим разработчика на устройстве.
2⃣Перейдите в "Настройки" -> "Для разработчиков".
3⃣Включите опцию "Profile GPU Rendering" и выберите "On screen as bars".
4⃣Запустите ваше приложение и наблюдайте за графиками. Зеленые линии указывают на время отрисовки, и ваша цель - оставаться ниже 16 мс (для 60 кадров в секунду).

🟠Android Profiler
Предоставляет набор инструментов для анализа производительности приложения.

1⃣ Откройте Android Studio и запустите ваше приложение.
2⃣Перейдите в "View" -> "Tool Windows" -> "Profiler".
3⃣Выберите ваше устройство и запущенное приложение.
4⃣Используйте вкладки CPU, Memory, Network и Energy для анализа различных аспектов производительности.
5⃣Сравните данные до и после оптимизации.

🟠Benchmarking
Создание и использование тестов производительности помогает количественно оценить улучшения. Вы можете использовать библиотеку Jetpack Benchmark для создания и выполнения тестов производительности.

🚩Пример использования Jetpack Benchmark:

1⃣Добавьте зависимости в build.gradle:
dependencies {
androidTestImplementation "androidx.benchmark:benchmark-junit4:1.1.0"
androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test:rules:1.3.0"
}


2⃣Создайте класс теста:
import androidx.benchmark.junit4.BenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun myFunctionBenchmark() {
benchmarkRule.measureRepeated {
// Вызов вашей функции или кода для тестирования производительности
myFunction()
}
}
}


🟠Logcat
Используйте журналирование для измерения времени выполнения определенных операций.
val startTime = System.currentTimeMillis()
// Ваш код
val endTime = System.currentTimeMillis()
Log.d("Performance", "Время выполнения: ${endTime - startTime} мс")


🟠StrictMode
StrictMode помогает обнаружить операции, которые могут замедлить работу приложения, такие как работа с сетью или базой данных в главном потоке.
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}


🟠Systrace
Systrace позволяет собирать и анализировать трассировки производительности системы, предоставляя детализированные данные о времени выполнения различных операций.

1⃣Включите режим разработчика на устройстве.
2⃣В "Настройки" -> "Для разработчиков" включите "Enable GPU Debug Layers".
3⃣Запустите команду adb shell am broadcast -a com.android.systemui.screenshot.ScreenshotService.ACTION_SYSTRACE.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Чем background service отличается от foreground service?

– Background service работает в фоне, может быть остановлен системой при нехватке ресурсов.
– Foreground service требует уведомление и имеет приоритет, не убивается системой просто так.


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

data class — класс для хранения данных с автогенерацией equals(), hashCode(), copy().
sealed class — ограниченная иерархия классов, используется для when.

🚩`data class` – для хранения данных

data class автоматически создаёт:
equals() и hashCode() → сравнение объектов по значениям.
copy() → удобное копирование с изменением параметров.
toString() → красивый вывод.
data class User(val id: Int, val name: String)

fun main() {
val user1 = User(1, "Alice")
val user2 = user1.copy(name = "Bob") // Создаём копию с новым именем

println(user1) // User(id=1, name=Alice)
println(user2) // User(id=1, name=Bob)
}


🚩`sealed class` – для ограниченных иерархий классов

sealed class используется, когда есть фиксированное число подклассов.
sealed class NetworkState {
object Loading : NetworkState()
data class Success(val data: String) : NetworkState()
data class Error(val message: String) : NetworkState()
}

fun handleState(state: NetworkState) {
when (state) {
is NetworkState.Loading -> println("Загрузка...")
is NetworkState.Success -> println("Данные: ${state.data}")
is NetworkState.Error -> println("Ошибка: ${state.message}")
}
}


🚩Можно ли использовать `data class` внутри `sealed class`?

Да! Это лучший вариант для управления состояниями:
sealed class Result {
object Loading : Result()
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}


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

1. Использовать onSaveInstanceState и передавать данные в Bundle.
2. Сохранить данные в ViewModel, чтобы они переживали пересоздание.
3. Сохранить данные в локальном хранилище, например, SharedPreferences.


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

В многомодульных проектах каждый модуль может иметь свой AndroidManifest.xml, чтобы:
Задавать зависимости (uses-permission, uses-feature) для конкретного модуля.
Определять компоненты (Activity, Service, BroadcastReceiver) для каждого модуля.
Автоматически объединять манифесты всех модулей в AndroidManifest.xml главного (app) модуля.

🚩Как объединяются манифесты в многомодульном проекте?

При сборке Gradle автоматически сливает (merge) все AndroidManifest.xml в один итоговый файл.
app/  
├── src/main/AndroidManifest.xml ← Главный манифест
├── java/com/example/MainActivity.kt
├── res/
├── build.gradle
feature_login/
├── src/main/AndroidManifest.xml ← Манифест модуля `login`
├── java/com/example/login/LoginActivity.kt
├── res/
├── build.gradle
feature_chat/
├── src/main/AndroidManifest.xml ← Манифест модуля `chat`
├── java/com/example/chat/ChatActivity.kt
├── res/
├── build.gradle


🚩Как работает объединение манифестов?

При сборке манифесты модулей сливаются в манифест главного модуля.
<manifest package="com.example.app">
<application>
<activity android:name=".MainActivity" />
</application>
</manifest>


feature_login (feature_login/src/main/AndroidManifest.xml)
<manifest>
<application>
<activity android:name=".login.LoginActivity" />
</application>
</manifest>


feature_chat (feature_chat/src/main/AndroidManifest.xml)
<manifest>
<application>
<activity android:name=".chat.ChatActivity" />
</application>
</manifest>


После объединения итоговый AndroidManifest.xml выглядит так
<manifest package="com.example.app">
<application>
<activity android:name=".MainActivity" />
<activity android:name=".login.LoginActivity" />
<activity android:name=".chat.ChatActivity" />
</application>
</manifest>


🚩Зачем модулям свой `AndroidManifest.xml`?

🟠Разные разрешения (`uses-permission`) для каждого модуля
Например, модуль camera требует CAMERA, но другие модули — нет.
<uses-permission android:name="android.permission.CAMERA" />


🟠Разные `Activity`, `Service`, `BroadcastReceiver` в каждом модуле
Каждый модуль добавляет только свои компоненты (например, LoginActivity в feature_login).

🟠Разные зависимости для разных модулей
Например, модуль feature_map использует Google Maps, но feature_login — нет.
<uses-library android:name="com.google.android.maps" />


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Какая особенность у Data Class относительно других Kotlin Classes?

Data Class автоматически генерирует equals, hashCode, toString, copy, и componentN функции. Они удобны для хранения данных и значительно упрощают работу с неизменяемыми структурами.


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

Лямбда-выражения представляют собой компактный способ представления анонимных функций, который особенно полезен для создания коротких блоков выполнения кода, предназначенных для последующей передачи другим функциям. Лямбды широко используются для упрощения работы с коллекциями, потоками данных, асинхронным кодом и в ситуациях, где требуется функциональный интерфейс.

🚩Лямбда-выражения

Были введены в Java 8 и являются частью усилий по добавлению функциональных возможностей в язык. Лямбды в Java чаще всего используются с функциональными интерфейсами, которые являются интерфейсами с одним абстрактным методом.
(parameters) -> expression


или
(parameters) -> { statements; }


🚩Лямбда-выражения

Поддерживает более гибкие и выразительные лямбды по сравнению с Java. Лямбды могут использоваться как с функциональными интерфейсами, так и в качестве части синтаксиса языка, благодаря чему Kotlin особенно удобен для функционального программирования.
{ parameters -> code body }


🚩Примеры

Лямбда без параметров
{ println("Hello, World!") }  


Лямбда с одним параметром
{ a: Int -> a * a }   


Лямбда с несколькими параметрами
{ a: Int, b: Int -> a + b }   


Лямбда с телом, содержащим несколько выражений
      { s: String ->
println(s)
s.length
}


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

volatile гарантирует, что все потоки видят актуальное значение переменной, а не её локальную копию из кеша. Это не делает операцию атомарной, но обеспечивает согласованность чтения/записи.


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

TransactionTooLargeException — это исключение, которое возникает, если передаваемые данные превышают лимит 1MB в Binder.
Чаще всего ошибка появляется при передаче больших объектов через Intent, Bundle, `onSaveInstanceState().

🚩Как воспроизвести `TransactionTooLargeException`?

Передача большого Bitmap через Intent (ОШИБКА!)
val bitmap: Bitmap = getLargeBitmap() //  Очень большой объект

val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // Ошибка: TransactionTooLargeException

startActivity(intent)


Ошибка
android.os.TransactionTooLargeException: data parcel size 2MB is larger than 1MB


🚩Как избежать `TransactionTooLargeException`?

Использовать FileProvider и Uri вместо Bitmap
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}

val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(intent)


🚩Использовать `ViewModel` вместо `onSaveInstanceState()`

Ошибка: сохранение больших данных в onSaveInstanceState()
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable("largeData", myLargeObject) // ОШИБКА!
super.onSaveInstanceState(outState)
}


Решение → Используем ViewModel, чтобы хранить данные в памяти:
class MyViewModel : ViewModel() {
var largeData: MyLargeObject? = null
}


🚩Хранить данные в `SharedPreferences` или `Room`

Если данные не нужны постоянно → используем SharedPreferences
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_data", jsonData)
.apply()


Если данные большие и важные → используем Room
@Dao
interface MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveData(data: MyData)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое try(BufferReader) { … } catch { … }?

Это пример try-with-resources — конструкции, которая автоматически закрывает ресурс, реализующий интерфейс AutoCloseable. Здесь BufferedReader закрывается автоматически по завершении блока, даже если возникло исключение. Это упрощает управление ресурсами и снижает риск утечек.


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

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


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