Kotlin | Вопросы собесов
2.57K subscribers
28 photos
964 links
Download Telegram
🤔 Что делать, если нужно, чтобы сервис продолжал работу в фоне?

Используйте startForegroundService для выполнения задач с высоким приоритетом.
1. Также можно применить WorkManager для длительных задач с учётом энергопотребления.
2. Для задач, требующих сетевого подключения, применяйте JobScheduler.


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

Когда используется deeplink, может возникнуть проблема, что одно и то же Activity может быть открыто несколько раз в стеке задач. Это происходит, если приложение запускается из внешнего источника (например, из браузера или другого приложения), и Android создает новую задачу или новую копию Activity вместо использования уже существующей. Чтобы справиться с этим, нужно правильно настроить launchMode, интенты и флаги.

🚩Причина проблемы

При запуске приложения через deep link, система Android может:
1. Создать новый экземпляр вашего Activity (даже если оно уже существует в стеке задач).
2. Поместить новую задачу в стек задач.
Если это не контролировать, пользователь может увидеть много дубликатов одного и того же Activity, что плохо для UX и может вызвать утечку памяти.

🚩Решения

🟠Использование launchMode в манифесте
В файле AndroidManifest.xml можно настроить поведение Activity с помощью атрибута launchMode:
singleTop: Если Activity уже находится на вершине стека, система не будет создавать новый экземпляр.
singleTask: Убедитесь, что только один экземпляр Activity существует в задаче. Если Activity уже существует, система передаст интент в метод onNewIntent().
singleInstance: Подходит для случаев, когда Activity должно быть абсолютно уникальным (используется редко).
<activity
android:name=".MyActivity"
android:launchMode="singleTop" />


🟠Использование флагов в интенте
Если вы используете deep link или запускаете Activity вручную, можно добавить флаги, чтобы управлять созданием экземпляров:
   val intent = Intent(this, MyActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)


🟠Обработка метода onNewIntent()
Если используется singleTop или singleTask, система вызывает метод onNewIntent(Intent intent) вместо создания нового экземпляра Activity. Этот метод можно переопределить для обработки новых данных из интента
   override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Обработка нового интента
val data = intent?.data
// Используйте данные deeplink
}


🟠Использование taskAffinity
Если требуется, чтобы разные deep link открывали одну и ту же задачу, можно настроить taskAffinity. Это нужно реже, но полезно, если нужно обрабатывать ссылки с разными контекстами.

🟠Проверка состояния Activity
Дополнительно можно вручную проверять, существует ли нужное Activity в текущем состоянии приложения, например, используя LiveData или ViewModel.

Файл AndroidManifest.xml
<activity
android:name=".MyActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.com"
android:path="/deeplink" />
</intent-filter>
</activity>


Файл MyActivity.kt
class MyActivity : AppCompatActivity() {

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Обработка данных из нового интента (deeplink)
val uri = intent?.data
uri?.let {
// Например, получить параметры из ссылки
val param = it.getQueryParameter("id")
Log.d("Deeplink", "Parameter id: $param")
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Как бы реализовал агрегацию на устройстве нескольких RSS-лент?

1. Настройка API для получения RSS-ленты через HTTP-запросы.
2. Парсинг данных, например, с использованием Jsoup или SAXParser.
3. Сохранение и обновление данных локально (Room или SharedPreferences).
4. Организация списка лент с возможностью фильтрации и уведомлений об обновлениях.


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

Проблемы с элементами списка в 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
👍3😁2
🤔 Какую бы выбрал стратегию по переходу большого проекта на Compose?

1. Гибридный подход: постепенная миграция, оставляя часть кода на XML и добавляя Compose для новых экранов.
2. Интеграция ComposeView: для использования Compose внутри существующих ViewGroup.
3. Модульная миграция: разделение приложения на модули и обновление каждого модуля поэтапно.
4. Тестирование на каждом этапе, чтобы избежать регрессий.


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

В Retrofit, чтобы передать значение в определенное место URL-адреса для GET-запроса, нужно использовать аннотацию @Path. Эта аннотация позволяет заменить параметр в строке пути на значение, переданное в метод.

🚩Как работает `@Path`?

1. Внутри аннотации @GET вы указываете строку пути, содержащую плейсхолдеры в фигурных скобках ({}).
2. Аннотация @Path связывает переменную метода с плейсхолдером в URL.
3. Когда метод вызывается, значение переменной подставляется вместо плейсхолдера в URL.

Допустим, у вас есть API с эндпоинтом
https://api.example.com/users/{id}/details


Настраиваем интерфейс Retrofit
interface ApiService {

@GET("users/{id}/details")
suspend fun getUserDetails(
@Path("id") userId: String
): Response<UserDetails>
}


Теперь, когда вы вызываете этот метод, вы можете передать значение для id, и Retrofit автоматически подставит его в URL
val apiService = retrofit.create(ApiService::class.java)

suspend fun fetchUserDetails() {
val userId = "123" // Пример значения
val response = apiService.getUserDetails(userId)
if (response.isSuccessful) {
println("User details: ${response.body()}")
} else {
println("Error: ${response.errorBody()?.string()}")
}
}


🚩Дополнительные аспекты использования `@Path`

🟠Как работают несколько параметров?
Вы можете использовать несколько плейсхолдеров в пути и связать их с несколькими параметрами с помощью @Path. Например:
API-эндпоинт
https://api.example.com/users/{userId}/posts/{postId}


Интерфейс Retrofit
interface ApiService {

@GET("users/{userId}/posts/{postId}")
suspend fun getPostDetails(
@Path("userId") userId: String,
@Path("postId") postId: String
): Response<PostDetails>
}


Вызов метода
val response = apiService.getPostDetails("123", "456")
// URL: https://api.example.com/users/123/posts/456


🟠Что если переменная null или пустая?
Если вы передаете null или пустую строку в @Path, это вызовет ошибку, так как Retrofit требует обязательного значения для всех параметров пути. Убедитесь, что передаваемое значение всегда валидно.

🟠Как избежать ошибок при форматировании?
Если значение пути может содержать символы, требующие экранирования (например, пробелы, специальные символы), Retrofit автоматически обработает это с помощью кодировки URL. Это делает использование @Path безопасным.
@GET("search/{query}")
suspend fun search(
@Path("query") searchQuery: String
): Response<SearchResults>


Если вы вызовете метод с поисковым запросом
apiService.search("hello world!")
// Retrofit закодирует URL: https://api.example.com/search/hello%20world%21


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

1. Однонаправленный поток данных — это модель, в которой данные передаются в одном направлении, от источника к получателю.
2. Используется в архитектуре Flux/Redux, где данные идут от состояния через действия к интерфейсу.
3. Такая модель улучшает предсказуемость и управление состоянием приложения.


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

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

🚩Когда наследование полезно

🟠Для повторного использования кода
Если у вас есть несколько классов, которые имеют общие свойства или методы, наследование позволяет вынести эти общие элементы в родительский класс. Это помогает избежать дублирования кода.

   open class Animal(val name: String) {
fun eat() {
println("$name ест.")
}
}

class Dog(name: String) : Animal(name) {
fun bark() {
println("$name лает.")
}
}

class Cat(name: String) : Animal(name) {
fun meow() {
println("$name мяукает.")
}
}

// Использование:
val dog = Dog("Бобик")
dog.eat() // "Бобик ест."
dog.bark() // "Бобик лает."


🟠Для создания иерархий классов (например, типологии)
Наследование отлично подходит для задач, связанных с созданием деревьев типов или категорий. Например, у вас есть базовый класс Shape (Форма), и от него наследуются конкретные фигуры: Circle, Rectangle, Triangle.

   open class Shape {
open fun draw() {
println("Рисуется форма.")
}
}

class Circle : Shape() {
override fun draw() {
println("Рисуется круг.")
}
}

class Rectangle : Shape() {
override fun draw() {
println("Рисуется прямоугольник.")
}
}

// Использование:
val shapes: List<Shape> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}


🟠Для использования полиморфизма
Полиморфизм позволяет использовать родительские классы для работы с объектами наследников. Это полезно, если у вас есть методы, которые работают с разными типами объектов, но с единым интерфейсом. Вызов методов draw() у всех объектов, не задумываясь об их конкретном типе, благодаря полиморфизму.

   fun render(shape: Shape) {
shape.draw()
}

render(Circle()) // "Рисуется круг."
render(Rectangle()) // "Рисуется прямоугольник."


🟠Когда концептуально существует отношение "is-a" (является)
Если наследник действительно является вариантом (подтипом) родительского класса, наследование логично. Например: Собака является животным (Dog is an Animal). Прямоугольник является фигурой (Rectangle is a Shape).

🚩Когда наследование не полезно

🟠Когда отношение "is-a" отсутствует
Если объекты не имеют строгого отношения "является", наследование использовать нельзя. Например: Класс Car (Машина) и Boat (Лодка) лучше объединить через общие свойства (интерфейсы или композицию), чем делать лодку наследником машины.

   open class Bird {
open fun fly() {
println("Птица летит.")
}
}

class Penguin : Bird() {
override fun fly() {
throw UnsupportedOperationException("Пингвин не умеет летать!")
}
}


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

   class Car : Engine, Wheels

Используйте композицию:

   class Car {
private val engine = Engine()
private val wheels = Wheels()
}


🟠Сложные иерархии классов
Если вы создаете слишком глубокие или разветвленные иерархии, код становится сложным для понимания, тестирования и изменения. Например, изменение в базовом классе может неожиданно затронуть всех наследников.

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

1. Делегирование позволяет передавать реализацию определённых функций или свойств другим объектам.
2. Используется ключевое слово by для передачи логики (например, class MyClass : Interface by Delegate).
3. Kotlin предоставляет встроенные делегаты, такие как lazy, observable и vetoable, для удобного управления состоянием свойств.


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

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

🚩Ограничения sealed классов

🟠Все подтипы должны быть определены в том же файле
Все классы, которые наследуются от sealed класса, должны быть определены в одном файле с самим sealed классом. Это ограничение введено для обеспечения полной контрольной иерархии и предотвращения добавления новых подтипов в другом месте.
sealed class Result {
class Success(val data: String) : Result()
class Error(val exception: Exception) : Result()
}


🟠Sealed класс не может быть интерфейсом
Sealed класс не может быть интерфейсом. Если вам нужно определить ограниченную иерархию интерфейсов, вы должны использовать обычные интерфейсы и sealed классы вместе.

🟠Sealed класс не может быть абстрактным классом напрямую
Хотя sealed классы и являются абстрактными по своей природе (их нельзя напрямую инстанцировать), они не могут быть явно помечены как abstract.

🟠Ограничение видимости
Sealed классы и их подтипы не могут быть private. Они должны быть либо public, либо internal, чтобы их можно было использовать в рамках всего файла иерархии.

🟠Sealed классы не поддерживают наследование от других классов
Sealed класс не может наследоваться от другого класса, кроме Any. Это связано с тем, что sealed классы уже имеют специфическое предназначение и их иерархия должна быть полностью определена в одном месте.

🟠Могут использоваться только для классов и объектов, но не интерфейсов
Sealed классы могут быть использованы для классов и объектов, но не могут использоваться для определения интерфейсов.

sealed class Operation {
class Addition(val value: Int) : Operation()
class Subtraction(val value: Int) : Operation()
object NoOp : Operation()
}

fun execute(operation: Operation, base: Int): Int {
return when (operation) {
is Operation.Addition -> base + operation.value
is Operation.Subtraction -> base - operation.value
Operation.NoOp -> base
}
}


🚩Плюсы

Компилятор знает все подтипы
Это позволяет использовать исчерпывающие выражения when, которые не требуют блока else, если все подтипы покрыты.
Полный контроль над иерархией классов
Это делает код более предсказуемым и безопасным.
Поддержка сопоставления с образцом (pattern matching)
Это делает код более читаемым и менее подверженным ошибкам.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🤯3👀21🤔1
🤔 Можно ли после by вызвать функцию или конструктор?

Нет, после by должен следовать уже существующий объект или класс, реализующий требуемый интерфейс.
1. Конструкторы или функции нельзя вызывать прямо в выражении by.
2. Сначала необходимо создать объект, а затем передать его в делегирование.


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

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

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

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

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

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

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

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

public class MyActivity extends AppCompatActivity {
private static TextView myTextView;

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

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


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

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

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


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

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

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

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

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

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

1. lateinit используется для откладывания инициализации переменных (только для var и только ссылочных типов).
2. Поле помечается как lateinit и должно быть инициализировано до первого использования, иначе выбрасывается UninitializedPropertyAccessException.
3. Это полезно в случаях, когда инициализация зависит от внешних условий (например, DI или тестирование).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥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
👍4🤯1
🤔 Что известно о double bang (!!)?

1. Оператор !! используется для явного указания, что значение не null.
2. При использовании !!, если значение оказывается null, выбрасывается исключение KotlinNullPointerException.
3. Рекомендуется избегать !! и использовать безопасные вызовы (?.) или оператор ?: для обработки null.


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

Да, можно создать анонимный класс внутри inline функции.
1. Однако, если этот класс захватывает переменные, они не будут инлайновыми и могут повлиять на производительность.
2. Inline функции лучше использовать для лямбда-функций, не создающих дополнительных объектов.


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

Ключевое слово object в Kotlin имеет несколько важных применений, и оно является одной из наиболее мощных и уникальных возможностей языка.

🟠Объявление объекта-одиночки (Singleton)
Kotlin предоставляет встроенную поддержку для создания singleton-объектов. Это объект, который имеет единственный экземпляр в приложении.
object Database {
val name = "MainDatabase"
fun connect() {
println("Подключение к базе данных $name")
}
}

fun main() {
Database.connect() // Подключение к базе данных MainDatabase
}


🟠Анонимные объекты
Ключевое слово object может использоваться для создания анонимных объектов (объектов без имени). Это полезно, если нужно создать временный объект или реализовать интерфейс/абстрактный класс.
fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата")
}
}
listener.onClick()
}

interface ClickListener {
fun onClick()
}


🟠Компаньон-объекты (Companion Object)
Ключевое слово object можно использовать внутри класса для объявления компаньон-объекта. Это позволяет создавать статические методы и переменные в классе.
class User(val name: String) {
companion object {
fun createGuest() = User("Guest")
}
}

fun main() {
val guest = User.createGuest()
println(guest.name) // Guest
}


🟠Объект-выражение
Ключевое слово object может использоваться для объявления объектов в коде прямо "на месте".
val myObject = object {
val x = 10
fun printX() {
println(x)
}
}

fun main() {
myObject.printX() // 10
}


🚩Почему `object` так полезен?

🟠Лаконичность
Вместо написания множества шаблонного кода для Singleton, анонимных объектов или статических методов, вы получаете готовую реализацию из коробки.

🟠Потокобезопасность
В случае object, компилятор Kotlin автоматически гарантирует, что объект создаётся в потокобезопасном режиме.

🟠Гибкость
object можно использовать практически везде: глобально, локально, внутри классов и функций.

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

1. MVC (Model-View-Controller): часто используется в старых приложениях.
2. MVP (Model-View-Presenter): разделяет бизнес-логику и представление.
3. MVVM (Model-View-ViewModel): основа Jetpack-компонентов, поддерживает двустороннюю привязку данных.
4. Clean Architecture: упрощает тестирование и масштабируемость через слои данных, домена и представления.


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

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

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

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

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

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

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

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

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

1. Все подклассы должны находиться в том же файле или быть вложенными.
2. Нельзя создавать экземпляры базового sealed класса напрямую.
3. Поддерживает ограниченное количество наследников, обеспечивая контроль над иерархией.


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