Sealed классы являются мощным инструментом для создания ограниченных иерархий классов, что обеспечивает безопасное и предсказуемое использование классов.
Все классы, которые наследуются от sealed класса, должны быть определены в одном файле с самим sealed классом. Это ограничение введено для обеспечения полной контрольной иерархии и предотвращения добавления новых подтипов в другом месте.
sealed class Result {
class Success(val data: String) : Result()
class Error(val exception: Exception) : Result()
} Sealed класс не может быть интерфейсом. Если вам нужно определить ограниченную иерархию интерфейсов, вы должны использовать обычные интерфейсы и sealed классы вместе.
Хотя sealed классы и являются абстрактными по своей природе (их нельзя напрямую инстанцировать), они не могут быть явно помечены как
abstract. Sealed классы и их подтипы не могут быть
private. Они должны быть либо public, либо internal, чтобы их можно было использовать в рамках всего файла иерархии.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, если все подтипы покрыты.Это делает код более предсказуемым и безопасным.
Это делает код более читаемым и менее подверженным ошибкам.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🤯3👀2❤1🤔1
1. Конструкторы или функции нельзя вызывать прямо в выражении by.
2. Сначала необходимо создать объект, а затем передать его в делегирование.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2💊1
Это ситуация, когда приложение выделяет память для каких-либо объектов, но затем не освобождает её, даже когда эти объекты больше не нужны. Это приводит к тому, что используемая память накапливается, что может со временем замедлить работу приложения и привести к его аварийному завершению (крашу) из-за нехватки доступной памяти.
В Android утечки памяти могут возникнуть из-за особенностей работы виртуальной машины (ART или Dalvik), а также из-за того, что сборщик мусора (Garbage Collector, GC) не может освободить память для объектов, на которые по-прежнему существуют ссылки. Это происходит в следующих случаях:
Если объект ссылается на другой объект, который больше не нужен, последний не может быть освобождён GC. Например:
Статические ссылки, которые продолжают "удерживать" объект.
Замыкания (closures), которые хранят ссылки на контекст активности.
Некоторые объекты системы Android, такие как
Context, View, Handler, хранят ссылки на компоненты приложения (например, Activity), из-за чего их нельзя освободить.Ошибки, такие как регистрация слушателей (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, если нужно сохранить ссылку, которая не должна блокировать сборщик мусора.Если вы регистрируете слушателей (например, через
setOnClickListener), обязательно удаляйте их в методах жизненного цикла, например, в onDestroy().Анонимные классы (например,
Runnable, Handler) могут неявно хранить ссылки на внешние классы, что может привести к утечке памяти.Android Profiler: встроенный инструмент Android Studio для мониторинга использования памяти. LeakCanary: библиотека, которая автоматически обнаруживает утечки памяти в вашем приложении.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
2. Поле помечается как lateinit и должно быть инициализировано до первого использования, иначе выбрасывается UninitializedPropertyAccessException.
3. Это полезно в случаях, когда инициализация зависит от внешних условий (например, DI или тестирование).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥1
Переменные по умолчанию не могут быть
null, что предотвращает NullPointerException.var a: String = "abc"
var b: String? = "abc"
b = null // Допустимо
Разделение на изменяемые и неизменяемые коллекции.
val list: List<String> = listOf("a", "b", "c") // Неизменяемый список
val mutableList: MutableList<String> = mutableListOf("a", "b", "c") // Изменяемый список Автоматическое создание методов
equals(), hashCode(), и toString().data class User(val name: String, val age: Int)
Автоматическое приведение типа после проверки с помощью
is.fun demo(x: Any) {
if (x is String) {
println(x.length)
}
} Упрощают обработку ограниченных иерархий классов.
sealed class Expr
data class Const(val number: Double) : Expr()
Kotlin автоматически определяет тип переменной.
val x = 10 // Int
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🤯1
2. При использовании !!, если значение оказывается null, выбрасывается исключение KotlinNullPointerException.
3. Рекомендуется избегать !! и использовать безопасные вызовы (?.) или оператор ?: для обработки null.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥2
1. Однако, если этот класс захватывает переменные, они не будут инлайновыми и могут повлиять на производительность.
2. Inline функции лучше использовать для лямбда-функций, не создающих дополнительных объектов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2
Ключевое слово
object в Kotlin имеет несколько важных применений, и оно является одной из наиболее мощных и уникальных возможностей языка. 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()
}Ключевое слово
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
}Вместо написания множества шаблонного кода для Singleton, анонимных объектов или статических методов, вы получаете готовую реализацию из коробки.
В случае
object, компилятор Kotlin автоматически гарантирует, что объект создаётся в потокобезопасном режиме.object можно использовать практически везде: глобально, локально, внутри классов и функций.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
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👍2❤1🤯1
Являются двумя различными типами коллекций, которые используются для хранения наборов данных. Основные различия между этими двумя структурами данных касаются их внутренней реализации, что влияет на производительность операций добавления, удаления и доступа к элементам.
основан на динамическом массиве. Это позволяет обеспечить быстрый доступ к элементам по индексу, поскольку адрес каждого элемента в памяти может быть вычислен напрямую. Однако, поскольку внутренний массив имеет фиксированный размер, при его переполнении необходимо выделить массив большего размера и скопировать в него все элементы из старого массива, что делает операции добавления и удаления более затратными по времени, особенно для больших объемов данных.
основан на двусвязном списке элементов, где каждый элемент (узел) содержит данные и ссылки на предыдущий и следующий элементы в списке. Это обеспечивает высокую производительность операций вставки и удаления, поскольку требуется лишь изменить ссылки у соседних элементов, но доступ к элементам по индексу занимает больше времени, так как для этого нужно последовательно пройти от начала или конца списка до нужного элемента.
Быстрый доступ к элементам по индексу. Медленные операции добавления и удаления элементов (особенно в начале и середине списка), так как может потребоваться сдвиг оставшейся части массива.
Быстрые операции вставки и удаления элементов, поскольку они требуют только изменения ссылок. Медленный доступ к элементам по индексу, так как для доступа к элементу необходимо пройти по списку.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2❤1
2. Нельзя создавать экземпляры базового sealed класса напрямую.
3. Поддерживает ограниченное количество наследников, обеспечивая контроль над иерархией.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤1👍1
Методы
get и replace в Kotlin относятся к работе с коллекциями, карты (Map) или к строкам. В зависимости от контекста, они вызываются через разные классы. Давайте разберем каждый случай отдельно.В контексте работы с картами (
Map), методы get и replace относятся к получению значений по ключу и замене существующих значений.Метод
getМетод
get используется для извлечения значения из карты по указанному ключу.val map = mapOf("key1" to "value1", "key2" to "value2")
println(map.get("key1")) // value1
println(map["key2"]) // value2 (альтернатива `get`)Метод
replace используется для обновления значения, связанного с определённым ключом, если он существует. Этот метод доступен только для изменяемых карт (MutableMap).val mutableMap = mutableMapOf("key1" to "value1", "key2" to "value2")
mutableMap.replace("key1", "newValue1")
println(mutableMap) // {key1=newValue1, key2=value2}В контексте строк, методы
get и replace работают с символами и подстроками.Метод
get используется для доступа к символу строки по индексу. Это альтернатива квадратным скобкам.val text = "Kotlin"
println(text.get(0)) // K
println(text[1]) // o (альтернатива `get`)
Метод
replace заменяет символы или подстроки в строке на заданные.val text = "Kotlin is awesome"
val newText = text.replace("awesome", "powerful")
println(newText) // Kotlin is powerful
Если вы пишете свои классы, вы можете переопределить оператор
get и метод replace, чтобы использовать их для своих нужд.class CustomList<T>(private val items: List<T>) {
operator fun get(index: Int): T {
return items[index]
}
}
fun main() {
val customList = CustomList(listOf(1, 2, 3))
println(customList[0]) // 1
}Пример с
replaceclass CustomMap<K, V>(private val map: MutableMap<K, V>) {
fun replace(key: K, value: V) {
if (map.containsKey(key)) {
map[key] = value
}
}
}
fun main() {
val customMap = CustomMap(mutableMapOf("key1" to "value1"))
customMap.replace("key1", "newValue1")
println(customMap) // {key1=newValue1}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1🔥1
2. Уменьшить количество преобразований строк.
3. Кешировать результаты парсинга для повторного использования.
4. Использовать специализированные библиотеки, такие как Moshi или Gson.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥2👍1
Чтобы начать рисовать пользовательский интерфейс (UI) на экране в Android-проекте, необходимо выполнить несколько шагов, которые включают настройку проекта, создание макета и взаимодействие с основными компонентами Android.
Первый шаг — создание Android-проекта в Android Studio. Это можно сделать, выбрав шаблон «Empty Activity», который предоставляет минимальный набор для разработки приложения.
Укажите имя проекта.
Выберите язык программирования (Java или Kotlin).
Убедитесь, что минимальная версия SDK подходит для вашей целевой аудитории.
Android UI в основном создаётся с использованием XML-файлов, которые определяют структуру интерфейса.
По умолчанию файл макета находится в каталоге:
res/layout/activity_main.xml
#### Пример простого макета:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Привет, мир!"
android:textSize="24sp"
android:padding="16dp"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Нажми меня"/>
</LinearLayout>
Макет нужно связать с логикой приложения в Java или Kotlin. Это делается с помощью метода
setContentView() в Activity.class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Указываем, какой XML-файл использовать для интерфейса
setContentView(R.layout.activity_main)
// Найдём элементы интерфейса и добавим логику
val textView = findViewById<TextView>(R.id.textView)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
textView.text = "Кнопка нажата!"
}
}
}Если нужно нарисовать что-то вручную (например, графику, линии, или кастомные фигуры), можно создать свой собственный класс, унаследованный от
View, и переопределить метод onDraw().class CustomView(context: Context) : View(context) {
private val paint = Paint().apply {
color = Color.RED
strokeWidth = 10f
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// Рисуем линию
canvas?.drawLine(100f, 100f, 400f, 400f, paint)
}
}Чтобы использовать этот класс, можно добавить его в макет или программно
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Устанавливаем кастомный View вместо макета
setContentView(CustomView(this))
}
}Теперь можно запустить приложение на эмуляторе или реальном устройстве:
Нажмите Run в Android Studio.
Убедитесь, что устройство подключено или выбран эмулятор.
После запуска вы увидите созданный интерфейс.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2🤯2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👀4👍1
В 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
👍8❤2🔥2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2❤1
Да, потеря состояния (state) может быть связана с фрагментами (Fragments) в Android. Это довольно распространённая проблема, особенно при работе с динамическими интерфейсами. Давайте разберёмся, почему она возникает, как её предотвратить и какие решения существуют.
Фрагменты имеют сложный жизненный цикл, который тесно связан с активностью. Основные этапы:
onCreate() — создаётся фрагмент, инициализируются объекты.onViewCreated() — создаётся View (UI компоненты).onStart() / onResume() — фрагмент становится видимым и активным.onPause() / onStop() — фрагмент приостанавливается.onDestroyView() — уничтожается только View (UI), но сам фрагмент всё ещё существует.onDestroy() — полностью уничтожается фрагмент.Фрагменты могут пересоздаваться системой, например, при смене ориентации экрана или нехватке памяти. Если разработчик неправильно сохраняет состояние фрагмента, оно может быть утеряно.
Когда фрагмент переходит в состояние
onDestroyView(), его View уничтожается, но сам объект фрагмента сохраняется. Если пользователь вернётся к этому фрагменту, View будет пересоздана, и вы потеряете все изменения, сделанные ранее, если они не сохранены явно.Использование
FragmentManager или FragmentTransaction с неправильными методами, такими как replace() или add(), без должного управления стэком (back stack), может привести к пересозданию или дублированию фрагментов, что вызывает потерю состояния.Фрагменты, как и активности, используют механизм сохранения состояния через
Bundle (в onSaveInstanceState). Если состояние не сохранено правильно, данные могут быть потеряны.При уничтожении фрагмента система вызывает метод
onSaveInstanceState(). Здесь вы можете сохранить важные данные в Bundle.override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key_text", editText.text.toString())
}При пересоздании фрагмента данные можно восстановить в
onViewCreated()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val restoredText = savedInstanceState?.getString("key_text")
editText.setText(restoredText)
}Для хранения состояния, которое переживает уничтожение и пересоздание фрагмента, лучше использовать архитектурный компонент
ViewModel.ViewModelclass MyViewModel : ViewModel() {
val textData = MutableLiveData<String>()
}class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// Восстановление данных
viewModel.textData.observe(viewLifecycleOwner) { text ->
editText.setText(text)
}
// Сохранение данных при изменении
editText.addTextChangedListener {
viewModel.textData.value = it.toString()
}
}
}При работе с фрагментами всегда добавляйте их в back stack, если вы хотите сохранить состояние.
val fragment = MyFragment()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit()
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
Чтобы обрабатывать жесты в Android, используйте класс
GestureDetector. Он помогает отслеживать стандартные жесты: одиночные нажатия, свайпы, долгие нажатия, двойные касания и т.д.Создайте экземпляр
GestureDetector, передав Context и слушателя (GestureDetector.OnGestureListener).Передавайте события касания в
gestureDetector.onTouchEvent(event) из метода onTouchEvent().class GestureActivity : AppCompatActivity(), GestureDetector.OnGestureListener {
private lateinit var gestureDetector: GestureDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gestureDetector = GestureDetector(this, this)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}
override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val deltaX = e2!!.x - e1!!.x
if (deltaX > 0) Log.d("Gesture", "Swipe Right") else Log.d("Gesture", "Swipe Left")
return true
}
override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) {}
override fun onSingleTapUp(e: MotionEvent?) = false
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent?) {}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Использует контент-провайдеры для обеспечения безопасности и контроля доступа к файлам. Прямые ссылки на файлы небезопасны, так как разные приложения имеют изолированные файловые системы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍2