1. Генерация данных.
2. Обработка данных через операторы.
3. Управление подпиской и отпиской.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
В
data class могут возникнуть проблемы со списками при использовании: Методов
copy() – список не копируется, а передаётся по ссылке. Методов
equals() и hashCode() – List сравнивается по элементам, что может быть медленно. Mutable списков (
MutableList) – изменения внутри списка изменяют все копии объекта. data class User(val name: String, val tags: List<String>)
fun main() {
val original = User("Alice", listOf("Admin", "Editor"))
val copy = original.copy() // ✅ Поверхностное копирование
println(original.tags === copy.tags) // true (один и тот же объект)
}
Используем
toList(), чтобы создать новый неизменяемый список: fun main() {
val original = User("Alice", listOf("Admin", "Editor"))
val copy = original.copy(tags = original.tags.toList())
println(original.tags === copy.tags) // false (разные объекты)
}data class Message(val id: Int, val content: String, val tags: List<String>)
fun main() {
val message1 = Message(1, "Привет", listOf("important", "urgent"))
val message2 = Message(1, "Привет", listOf("important", "urgent"))
println(message1 == message2) // true (сравнивает элементы списка)
}
Если идентификатор (
id) уникален, сравнивать только его data class Message(val id: Int, val content: String, val tags: List<String>) {
override fun equals(other: Any?) = other is Message && this.id == other.id
override fun hashCode() = id.hashCode()
}data class Task(val name: String, val subtasks: MutableList<String>)
fun main() {
val original = Task("Купить продукты", mutableListOf("Хлеб", "Молоко"))
val copy = original.copy() // ✅ Поверхностная копия
copy.subtasks.add("Яйца") // ❌ Меняет список в обоих объектах!
println(original.subtasks) // [Хлеб, Молоко, Яйца]
println(copy.subtasks) // [Хлеб, Молоко, Яйца]
}
Создаём новый
MutableList внутри copy() data class Task(val name: String, val subtasks: List<String>) {
fun deepCopy() = Task(name, subtasks.toMutableList())
}
fun main() {
val original = Task("Купить продукты", mutableListOf("Хлеб", "Молоко"))
val copy = original.deepCopy()
copy.subtasks.toMutableList().add("Яйца") // ✅ Теперь изменения не влияют на оригинал
println(original.subtasks) // [Хлеб, Молоко]
println(copy.subtasks) // [Хлеб, Молоко, Яйца]
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6💊1
- Переходит в паузу, затем в onStop().
- Остаётся в стеке задач.
- Может быть уничтожена системой при нехватке памяти.
Если используешь finish() — она будет удалена немедленно.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Если удалился элемент из списка, нужно:
Удалить его из списка данных.
Сообщить
Adapter, чтобы он перерисовал только изменённые элементы. Этот метод обновляет только удалённый элемент, сохраняя анимации.
fun removeItem(position: Int) {
itemList.removeAt(position) // Удаляем из списка данных
notifyItemRemoved(position) // Сообщаем адаптеру
notifyItemRangeChanged(position, itemList.size) // Обновляем индексы
}Если изменился несколько элементов, лучше использовать
DiffUtil, чтобы обновить только нужные элементы. class MyAdapter : ListAdapter<Item, MyViewHolder>(DIFF_CALLBACK) {
companion object {
private val DIFF_CALLBACK = object : 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
}
}
}
}Обновляем список через
submitList() fun removeItem(item: Item) {
val newList = currentList.toMutableList()
newList.remove(item)
submitList(newList) // `DiffUtil` сам вычислит, что обновить
}Использовать только если изменился весь список!
fun removeItem(position: Int) {
itemList.removeAt(position)
notifyDataSetChanged() // Перерисовывает весь список (медленно)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Реализация может быть следующей:
- Загрузка на старте экрана: при запуске фрагмента/экрана уже начинать сетевой запрос.
- Фоновая загрузка при idle-состоянии: использовать WorkManager или CoroutineScope для подгрузки ночью, в фоне и т.п.
- RecyclerView prefetch: через RecyclerView.setItemViewCacheSize() и PrefetchingLayoutManager заранее подгружаются карточки.
- Кеширование: сохранить данные в БД или памяти при первом получении, и отображать в UI до завершения запроса.
Цель — снизить видимое пользователю ожидание.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
В Java и Kotlin существуют различные типы данных, которые можно разделить на две основные категории:
Примитивные типы данных (primitive types)
Ссылочные типы данных (reference types)
Ссылочные типы данных указывают на объекты. Они хранятся в куче (heap) и содержат ссылку (адрес) на объект.
Примеры ссылочных типов
String str = "Hello";MyClass obj = new MyClass();int[] arr = {1, 2, 3};List<Integer> list = new ArrayList<>();В Kotlin типы данных делятся на nullable и non-nullable (значения, которые могут быть
null, и те, которые не могут). Kotlin избегает примитивных типов напрямую, но на уровне JVM использует их для оптимизации. Kotlin предоставляет высокоуровневые обёртки для примитивных типов. Например:
Int вместо intDouble вместо doubleВ Kotlin все данные — объекты.
Сюда входят:
Строки (
String): val str: String = "Hello"Коллекции:
val list: List<Int> = listOf(1, 2, 3)Классы:
val obj = MyClass()Nullable-типизация: В Kotlin можно указать, что переменная может быть
null. Для этого используется ?. val nullableString: String? = null
val nonNullableString: String = "Hello"
Интерфейсы и абстракции: Kotlin поддерживает классы, интерфейсы и их реализации, например
interface Animal {
fun makeSound()
}
class Dog : Animal {
override fun makeSound() {
println("Woof!")
}
}
Kotlin добавляет уникальные типы, которые отсутствуют в Java:
Unit
Эквивалент
void в Java, но является объектом.Используется, когда функция ничего не возвращает:
fun printMessage(message: String): Unit {
println(message)
}
NothingПредставляет значение, которое никогда не будет существовать (например, функция всегда бросает исключение):
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
associateBy создаёт Map, где ключи берутся из заданной логики, а значениями становятся элементы коллекции. associateWith наоборот — элемент становится ключом, а значение задаётся отдельно.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2
Когда используется deeplink, может возникнуть проблема, что одно и то же Activity может быть открыто несколько раз в стеке задач. Это происходит, если приложение запускается из внешнего источника (например, из браузера или другого приложения), и Android создает новую задачу или новую копию Activity вместо использования уже существующей. Чтобы справиться с этим, нужно правильно настроить
launchMode, интенты и флаги.При запуске приложения через deep link, система Android может:
1. Создать новый экземпляр вашего Activity (даже если оно уже существует в стеке задач).
2. Поместить новую задачу в стек задач.
Если это не контролировать, пользователь может увидеть много дубликатов одного и того же Activity, что плохо для UX и может вызвать утечку памяти.
В файле
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)
Если используется
singleTop или singleTask, система вызывает метод onNewIntent(Intent intent) вместо создания нового экземпляра Activity. Этот метод можно переопределить для обработки новых данных из интента override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// Обработка нового интента
val data = intent?.data
// Используйте данные deeplink
}
Если требуется, чтобы разные deep link открывали одну и ту же задачу, можно настроить
taskAffinity. Это нужно реже, но полезно, если нужно обрабатывать ссылки с разными контекстами.Дополнительно можно вручную проверять, существует ли нужное 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.ktclass 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
👍2🔥1
- data class — для хранения и работы с данными, удобны при копировании, логическом сравнении, сериализации.
- sealed class — для ограниченного набора подтипов. Удобны при использовании when, так как все случаи должны быть обработаны — это повышает безопасность и читаемость.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
В Kotlin есть множество удобных функций для получения одного элемента или списка элементов из коллекций (
List, Set, Map). Какую функцию использовать — зависит от задачи. получение по индексу
Если индекс может выйти за границы массива, лучше использовать
getOrNull(). val list = listOf("A", "B", "C")
println(list[1]) // "B"
println(list.getOrNull(5)) // nullпервый и последний элементы
println(list.first()) // "A"
println(list.last()) // "C"
Используйте
firstOrNull() или lastOrNull(), если список может быть пустымprintln(emptyList<String>().firstOrNull()) // null
поиск по условию Возвращает первый или последний элемент, удовлетворяющий условию.
val numbers = listOf(10, 20, 30, 40)
println(numbers.find { it > 15 }) // 20
println(numbers.findLast { it > 15 }) // 40
println(numbers.find { it > 50 }) // null
если ожидается только один элемент
val oneElementList = listOf(42)
println(oneElementList.single()) // 42
println(oneElementList.singleOrNull()) // 42
val emptyList = emptyList<Int>()
println(emptyList.singleOrNull()) // null
single() бросает исключение, если элементов нет или их больше одного. filter {} – фильтрация элементов по условиюval numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // [2, 4]
take(n) и takeLast(n) – первые/последние n элементов val items = listOf("a", "b", "c", "d", "e")
println(items.take(3)) // [a, b, c]
println(items.takeLast(2)) // [d, e]slice(indices) – получение элементов по индексам val sliced = items.slice(listOf(0, 2, 4))
println(sliced) // [a, c, e]
map {} – преобразование списка val squared = numbers.map { it * it }
println(squared) // [1, 4, 9, 16, 25]Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Нет, после
by нельзя вызывать функции или конструкторы. by ожидает готовый объект, который реализует интерфейс или делегирует свойство. В Kotlin
by используется в двух случаях: Делегирование интерфейсов (
class A : Interface by obj) Делегирование свойств (
val x by lazy {}) Но
by не может вызывать функции или конструкторы – ему нужен уже созданный объект. Правильный код (делегируем готовый объект)
interface Printer {
fun printMessage()
}
class RealPrinter : Printer {
override fun printMessage() = println("Печать...")
}
// ✅ Делегируем готовый объект `RealPrinter()`
class MyClass : Printer by RealPrinter()
fun main() {
MyClass().printMessage() // Печать...
}Ошибка, если после
by вызвать конструктор class MyClass : Printer by RealPrinter() // ✅ Работает
class MyClass2 : Printer by RealPrinter() { } // ❌ Ошибка!
Правильный код (
by lazy) val message: String by lazy { "Привет, мир!" }
println(message) // Привет, мир!Ошибка, если после
by вызвать функцию val message: String by getMessage() // ❌ Ошибка!
fun getMessage() = "Привет, мир!"
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊8👍2
- Через .equals() — логическое сравнение содержимого.
- При переопределении .equals() рекомендуется также переопределить .hashCode().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1💊1
В Java все классы неявно наследуются от класса
Object, если явно не указано другое наследование. class MyClass {
// Неявно наследуется от Object
}
class MyClass2 extends Object {
// То же самое, просто указано явно
}Класс
Object содержит основные методы, доступные во всех классах: class Person {
String name;
Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Alice");
System.out.println(p.toString()); // Person{name='Alice'}
System.out.println(p.hashCode()); // Хеш-код объекта
System.out.println(p.getClass()); // class Person
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
- Сделать приватный конструктор, чтобы запретить внешнее создание объекта.
- Используется в паттерне Singleton или Utility-классах (например, Math).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1💊1
В
interface можно определять свойства (val или var), но их реализация зависит от того, есть ли у них get и set. В интерфейсе можно объявить
val (только для чтения) или var (для чтения и записи), но без инициализации. interface User {
val name: String // Объявляем свойство, но не реализуем
var age: Int // Может изменяться, но без начального значения
}Использование в классе
class Person(override val name: String) : User {
override var age: Int = 25 // Обязательно реализовать
}Можно задать кастомный геттер прямо в интерфейсе.
interface User {
val name: String
val greeting: String
get() = "Привет, $name!" // Реализация внутри интерфейса
}Использование в классе
class Person(override val name: String) : User
fun main() {
val user = Person("Андрей")
println(user.greeting) // Привет, Андрей!
}
Если в интерфейсе объявить
var, то нельзя задать реализацию геттера и сеттера — их должен реализовать класс. interface User {
var age: Int // Только объявление, без реализации
}Использование в классе
class Person : User {
override var age: Int = 30 // Реализуем свойство
}В
interface нельзя использовать field (бэкинг-поле), потому что у интерфейсов нет состояния. interface User {
var age: Int
get() = field // Ошибка! Нельзя использовать `field` в интерфейсе
set(value) { field = value }
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Для этого используется ItemAnimator — по умолчанию это DefaultItemAnimator. Также можно использовать DiffUtil и ListAdapter, чтобы RecyclerView самостоятельно определял изменения и плавно их анимировал.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
При пересоздании
Activity (например, при повороте экрана) состояние ScrollView или RecyclerView сбрасывается. Чтобы этого избежать, можно сохранить и восстановить позицию скролла. Android позволяет сохранять данные в
Bundle перед уничтожением Activity, а затем восстанавливать их. class MainActivity : AppCompatActivity() {
private lateinit var scrollView: ScrollView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
scrollView = findViewById(R.id.scrollView)
// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollY = it.getInt("scroll_position", 0)
scrollView.post { scrollView.scrollTo(0, scrollY) }
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("scroll_position", scrollView.scrollY)
}
}У
RecyclerView уже есть встроенный механизм сохранения состояния через LinearLayoutManager. class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var layoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
layoutManager = LinearLayoutManager(this)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = MyAdapter()
// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollPosition = it.getInt("recycler_position", 0)
recyclerView.post { layoutManager.scrollToPosition(scrollPosition) }
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("recycler_position", layoutManager.findFirstVisibleItemPosition())
}
}Если
EditText находится внутри ScrollView, можно включить android:freezesText="true", чтобы автоматически сохранять данные. <EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:freezesText="true"/>
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Эта функция делит коллекцию на два списка: один содержит элементы, соответствующие условию, другой — не соответствующие. Это удобно для фильтрации без потери невалидных элементов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍1
Лямбда-выражения представляют собой компактный способ представления анонимных функций, который особенно полезен для создания коротких блоков выполнения кода, предназначенных для последующей передачи другим функциям. Лямбды широко используются для упрощения работы с коллекциями, потоками данных, асинхронным кодом и в ситуациях, где требуется функциональный интерфейс.
Были введены в 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