Kotlin | Вопросы собесов
2.57K subscribers
28 photos
965 links
Download Telegram
Конструкторы нужны для инициализации свойств. В data class основной конструктор обязателен, так как он используется для equals, copy, toString и hashCode. Обойтись совсем без конструктора нельзя — хотя можно использовать значения по умолчанию.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2🔥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
👍1
🤔 Какие модификаторы доступа могут быть в типах в Kotlin?

- public — доступен везде (по умолчанию).
- internal — доступен в пределах модуля.
- protected — доступен внутри класса и подклассов.
- private — доступен внутри файла или класса.


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

Основные операторы:
- merge — объединяет элементы из нескольких источников.
- concat — объединяет источники последовательно.
- zip — комбинирует элементы из разных источников попарно.
- combineLatest — объединяет последние элементы.
- switchMap — переключается на новый поток и отменяет предыдущий.
- amb — выбирает тот источник, который быстрее начнёт эмиссию.
- join, groupJoin — объединяют потоки с логикой по времени жизни элементов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
👍4
🤔 В чём разница между FragmentManager и FragmentTransaction?

- FragmentManager управляет фрагментами: добавление, поиск, стек возврата.
- FragmentTransaction используется для выполнения операций с фрагментами (добавление, замена, удаление). Это как "транзакция базы данных", применяемая к UI.
Transaction создаётся через FragmentManager.beginTransaction() и применяется через commit().


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

- Компоненты — это точки входа в граф зависимостей. Через них получаем зависимости и связываем граф с приложением.
- Модули — классы, предоставляющие зависимости через
@Provides или @Binds.
Они позволяют:
- Разделять зависимости по слоям (data, domain, UI).
- Управлять временем жизни объектов.
- Повторно использовать код в разных частях приложения.


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

Сборщик мусора (Garbage Collector, GC) в Android (и в JVM) использует анализ ссылок для определения, можно ли уничтожить объект.

🚩Объект считается "мёртвым", если на него нет доступных ссылок

GC работает по принципу "сборки мусора с поиском корней" (Tracing Garbage Collection).

GC ищет "корневые" объекты (Root Objects) – это объекты, к которым точно есть ссылка (например, статические переменные, локальные переменные текущего потока, объекты в стеке).
GC обходит все объекты, к которым есть ссылки (прямые или косвенные).
Если объект не связан с корневыми объектами, он считается "мусором" и удаляется.
fun main() {
var user: User? = User("Alice") // Создаём объект
user = null // Теперь на объект нет ссылок, GC его удалит
}


🚩Сборщик мусора использует "Mark & Sweep"

Метод Mark & Sweep – основной алгоритм работы GC.
Mark (Пометка) – GC помечает все достижимые объекты (к которым есть ссылки).
Sweep (Очистка) – GC удаляет непомеченные объекты (на которые нет ссылок).
Root → A → B
→ C


Недостижимые объекты (GC их удаляет)
Root → A → B (C больше недоступен)

C (GC удалит!)


🚩"Сборка поколений" (Generational GC)

Объекты делятся на молодые (Young) и старые (Old)
Young Generation – новые объекты (большинство умирает быстро).
Old Generation – "долго живущие" объекты (Activity, Singleton).

🚩Что GC НЕ удаляет? (Memory Leaks)

Утечки памяти (Memory Leaks) происходят, если на объект осталась ссылка, но он больше не нужен.
class Activity {
var button: Button? = null
}

var activity: Activity? = Activity() // Создаём объект
activity?.button = Button() // `Button` ссылается на `Activity`
activity = null // Activity нельзя удалить из-за ссылки на кнопку!


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

В Kotlin switch заменён на when. Это более мощный инструмент с поддержкой:
- Значений.
- Диапазонов.
- Условий.
- Множественных веток.


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

Google постоянно ограничивает работу сервисов в Android, чтобы:
Уменьшить расход батареи
Оптимизировать использование памяти
Защитить пользователя от фоновых процессов, "убивающих" производительность

🟠Запрет на запуск сервисов в фоне (Android 8+)
Сервис нельзя запустить из фона, если приложение не активно.
startService(Intent) выдаст ошибку, если приложение не на переднем плане.
1. Использовать Foreground Service (с уведомлением).
2. Использовать JobIntentService / WorkManager.
class MyForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, "channelId")
.setContentTitle("Сервис работает")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()

startForeground(1, notification) // Запускаем сервис в Foreground
}

override fun onBind(intent: Intent?): IBinder? = null
}


🟠Запрет на старт сервисов из фона (Android 10+)
Если сервис запущен из фона (например, через BroadcastReceiver), он не запустится.
Исключение – если сервис работает в Foreground или с AlarmManager.
1. Использовать WorkManager (лучший вариант).
2. Использовать Foreground Service с уведомлением.
3. Использовать AlarmManager для периодических задач.

🟠Ограничение работы сервисов в спящем режиме (Doze Mode, Android 6+)
В спящем режиме (Doze Mode) система отключает сервисы.
Приложения не могут выполнять фоновые задачи.
Использовать Foreground Service.
Использовать JobScheduler / WorkManager.
Использовать Firebase Cloud Messaging (FCM) для пробуждения приложения.

🟠Запрет на работу сервисов после закрытия приложения (Android 9+)
Если пользователь смахнул приложение из списка недавних, фоновые сервисы будут убиты.
Решение → Foreground Service + startForeground() или JobScheduler.

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

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

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

Android-приложение состоит из четырёх основных компонентов:
Activity – UI-экран приложения.
Service – фоновая работа без UI.
BroadcastReceiver – слушает системные и пользовательские события.
ContentProvider – делится данными между приложениями.

🚩`Activity` – экран приложения (UI)

Отображает интерфейс пользователя.
Обрабатывает взаимодействие (нажатия, свайпы, ввод текста).
Управляется системой через жизненный цикл (Lifecycle).
Пример Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Загружаем XML-разметку
}
}


🚩`Service` – фоновая работа без UI

Выполняет длительные фоновые задачи (музыка, загрузка файлов).
Может работать даже если приложение закрыто.
НЕ имеет UI (не рисует View).
Пример Service
class MyService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Thread {
// Долгая фоновая задача
}.start()
return START_STICKY
}

override fun onBind(intent: Intent?): IBinder? = null
}


Запуск сервиса
startService(Intent(this, MyService::class.java))


Остановка сервиса
stopService(Intent(this, MyService::class.java))


🚩`BroadcastReceiver` – обработка событий (системных и пользовательских)

- Получает события системы (BOOT_COMPLETED, BATTERY_LOW).
- Слушает события других приложений.
- Может быть динамическим (через код) или статическим (AndroidManifest.xml).
Пример BroadcastReceiver (отключение Wi-Fi)
class NetworkReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
println("Сеть изменилась!")
}
}
}


Регистрируем BroadcastReceiver в AndroidManifest.xml
<receiver android:name=".NetworkReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>


Можно зарегистрировать динамически в коде
val receiver = NetworkReceiver()
registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))


🚩`ContentProvider` – обмен данными между приложениями

- Дает доступ к данным *другим приложениям.
- Позволяет безопасно работать с БД (Room, SQLite).
- Используется для Contacts, MediaStore, Calendar.

Пример ContentProvider (чтение контактов)
val cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
println("Контакт: $name")
}
}


Запрос данных через Uri
val uri = Uri.parse("content://com.example.provider/table")
contentResolver.query(uri, null, null, null, null)


🚩Как компоненты взаимодействуют между собой?

Пример: Activity запускает Service через Intent
startService(Intent(this, MyService::class.java))

Пример: Service отправляет Broadcast, а Receiver его ловит
sendBroadcast(Intent("com.example.CUSTOM_EVENT"))


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

Объекты, которые больше не имеют активных ссылок, считаются "мусором". Такие объекты автоматически удаляются сборщиком мусора (Garbage Collector), освобождая память для новых объектов. Это происходит в фоновом режиме и не требует прямого вмешательства разработчика.

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

RxJava может вызывать утечки памяти, если подписки (Disposable) не очищаются правильно.
Утечки памяти происходят, когда RxJava-события продолжают выполняться после уничтожения Activity/Fragment, удерживая ссылки на View или Context.

🚩Используем `CompositeDisposable`

CompositeDisposable собирает все подписки и очищает их при onDestroy().
class MainActivity : AppCompatActivity() {
private val disposables = CompositeDisposable() // Собираем подписки

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }

disposables.add(disposable) // Добавляем подписку
}

override fun onDestroy() {
super.onDestroy()
disposables.clear() // Очищаем подписки, предотвращая утечку памяти
}
}


🚩Используем `autoDispose` (для Jetpack Lifecycle)

Если используем Jetpack ViewModel или LifecycleOwner, можно автоматически отвязывать подписки.
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Observable.interval(1, TimeUnit.SECONDS)
.autoDispose(viewLifecycleOwner) // Автоочистка при `onDestroyView()`
.subscribe { println("Tick: $it") }
}
}


🚩Используем `Disposable.dispose()` вручную

Если подписка не в CompositeDisposable, её можно очистить вручную.
private var disposable: Disposable? = null

fun startObserving() {
disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }
}

fun stopObserving() {
disposable?.dispose() // Очищаем подписку
}


🚩Используем `ViewModel` + `LiveData` вместо RxJava

RxJava может удерживать Activity/Fragment, вызывая утечки памяти.
Лучше использовать ViewModel + LiveData!
class MyViewModel : ViewModel() {
private val _timer = MutableLiveData<Long>()
val timer: LiveData<Long> = _timer

init {
Observable.interval(1, TimeUnit.SECONDS)
.subscribe { _timer.postValue(it) }
}
}


Подписка в Activity (без утечек!)
viewModel.timer.observe(this) { println("Tick: $it") }


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

Порядок:
1. Статический блок суперкласса;
2. Статический блок текущего класса;
3. Конструктор суперкласса;
4. Конструктор текущего класса.
(Статические блоки вызываются один раз при загрузке класса.)


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

Модификации могут означать разные вещи в зависимости от контекста. В Android-разработке чаще всего речь идёт о следующих типах модификаций:

Модификация данных (изменение состояния)
Модификация UI (Compose Modifiers)
Модификация кода (рефакторинг, оптимизация)
Модификация системы (кастомные прошивки, рут-изменения)

🟠Модификация данных (изменение состояния)
Это изменение переменных, объектов и структур данных во время работы приложения.
class ViewModel : ViewModel() {
private val _state = MutableStateFlow("Привет, мир!")
val state: StateFlow<String> = _state

fun updateText(newText: String) {
_state.value = newText // Модификация состояния
}
}


🟠Модификация UI (Jetpack Compose Modifiers)
В Jetpack Compose модификации (Modifier) используются для изменения внешнего вида и поведения UI-элементов.
Text(
text = "Привет!",
modifier = Modifier
.padding(16.dp) // Добавляем отступ
.background(Color.Blue) // Меняем фон
.clickable { println("Нажали!") } // Добавляем клик
)


🟠Модификация кода (рефакторинг, оптимизация)
В программировании модификация кода означает его улучшение без изменения основной логики.
fun sum(a: Int, b: Int): Int {
return a + b
}


После рефакторинга
fun sum(a: Int, b: Int) = a + b


🟠Модификация системы (кастомные прошивки, рут-изменения)
Это изменения в самой операционной системе Android, например:
- Рут-доступ (root), позволяющий изменять системные файлы.
- Кастомные прошивки (например, LineageOS).
- Изменение build.prop для скрытия рут-доступа.
ro.build.version.sdk=33


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7👍1
🤔 Зачем нужна аннотация @CallSuper?

Аннотация
@CallSuper указывает, что метод в подклассе должен вызывать метод суперкласса. Это полезно при переопределении методов, где требуется сохранить логическую цепочку вызовов (например, onStart(), onDestroy() в Android).

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

Методы equals() и hashCode() используются для сравнения объектов и их корректной работы в коллекциях (Set, Map).

🚩Контракт `equals()`

Метод equals() должен:
Рефлексивность: a.equals(a)true (объект равен самому себе).
Симметричность: a.equals(b) == b.equals(a).
Транзитивность: если a == b и b == c, то a == c.
Согласованность: если a == b, то a.equals(b) всегда возвращает одно и то же, пока объект не изменится.
Сравнение с null всегда даёт false: a.equals(null) == false.
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true // Проверка на ссылочное равенство
if (other !is User) return false // Проверка типа

return name == other.name && age == other.age // Сравнение полей
}
}


val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (потому что переопределён equals)


🚩Контракт `hashCode()`

Метод hashCode() должен:
Согласованность с equals(): если a == b, то a.hashCode() == b.hashCode().
Но обратное не обязательно: два разных объекта могут иметь одинаковый hashCode().
Хеш-код не должен меняться, если объект не изменился.
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is User) return false
return name == other.name && age == other.age
}

override fun hashCode(): Int {
return name.hashCode() * 31 + age // 31 - стандартный коэффициент
}
}


val userSet = HashSet<User>()
userSet.add(User("Alice", 25))
println(userSet.contains(User("Alice", 25))) // true


🚩Автоматическая генерация в Kotlin

Чтобы не писать equals() и hashCode() вручную, можно использовать data class:
data class User(val name: String, val age: Int)


data class автоматически создаёт equals(), hashCode(), а также copy() и toString().
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (equals)
println(user1.hashCode() == user2.hashCode()) // true


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

Наследование — это когда один класс расширяет другой, наследуя его поведение.
Композиция — это включение одного объекта в другой, при этом зависимость явно передаётся. Композиция считается более гибкой и менее связанной альтернативой наследованию.


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