Star Projection (*) используется в Kotlin Generics, когда точный тип параметра неизвестен.
- List<*> означает List с элементами любого типа, но доступ к ним возможен только для чтения.
- Используется для работы с универсальными API, где нет строгой типизации (MutableList<out T>).
Применяется в коллекциях и функциях, работающих с неизвестными дженериками.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Нет, после
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
❤1👍1🤯1
Это механизм контроля потока данных, предотвращающий перегрузку потребителя (consumer), если источник (producer) отправляет данные слишком быстро.
В Kotlin Flow и Reactive Streams (RxJava, Project Reactor) есть стратегии управления Back Pressure:
- Buffer – накапливает данные в памяти.
- Drop – игнорирует избыточные элементы.
- Latest – хранит только последний элемент.
- Conflate – объединяет значения для уменьшения нагрузки.
Back Pressure нужен для стабильности и предотвращения утечек памяти в асинхронных потоках.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2❤1
При создании классов по сравнению с Java произошли несколько значительных изменений и упрощений. Kotlin предлагает более лаконичный и выразительный синтаксис, что делает код более читаемым и удобным.
В Kotlin объявление классов и их конструкторов значительно упрощено.
В Java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}В Kotlin
class Person(val name: String, val age: Int)
В Java для объявления статических членов используется ключевое слово
static. В Kotlin вместо этого используются companion object.В Java
public class MyClass {
public static final String CONSTANT = "constant";
public static void staticMethod() {
// Some code
}
}В Kotlin
class MyClass {
companion object {
const val CONSTANT = "constant"
@JvmStatic
fun staticMethod() {
// Some code
}
}
}Kotlin предоставляет специальный тип классов —
data классы, которые автоматически генерируют методы equals(), hashCode(), toString(), copy(), и componentN().В Java
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// Implementation
}
@Override
public int hashCode() {
// Implementation
}
@Override
public String toString() {
// Implementation
}
}В Kotlin
data class User(val name: String, val age: Int)
В Kotlin свойства объявляются напрямую, и методы доступа (геттеры и сеттеры) генерируются автоматически.
В Java
public class Rectangle {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}В Kotlin
class Rectangle(var width: Int, var height: Int)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2🔥2🤔1
Forwarded from easyoffer
На easyoffer 2.0 появится:
База тестовых заданий
🟠 Тестовые задания для разных грейдов
🟠 Фильтрация тестовых заданий по технологиям и компаниям
Когда я только начинал учиться на программиста, я постоянно выдумывал себе задачи для практики и тратил на это много времени. Но только в момент поиска работы я столкнулся с тестовыми заданиями, и понял насколько круто они прокачивают навыки. Нужно было еще на этапе обучения пробовать их делать. Все компании стараются составить тестовое задание "под себя", это дает большой выбор в тематике задач и технологий. На easyoffer 2.0 вы сможете отфильтровать тестовые задания по навыкам/грейдам и найти те, что подходят лично вам для практики.
В течение 1-2 дней я объявлю о краудфандинг кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки и смогут попасть на закрытое бета-тестирование. А первые 150 донатеров получать особо-выгодную цену и бонус.
🚀 Следите за стартом 👉 в этом телеграм канале, в нем информация о старте будет опубликована за 6 часов до официального начала.
База тестовых заданий
Когда я только начинал учиться на программиста, я постоянно выдумывал себе задачи для практики и тратил на это много времени. Но только в момент поиска работы я столкнулся с тестовыми заданиями, и понял насколько круто они прокачивают навыки. Нужно было еще на этапе обучения пробовать их делать. Все компании стараются составить тестовое задание "под себя", это дает большой выбор в тематике задач и технологий. На easyoffer 2.0 вы сможете отфильтровать тестовые задания по навыкам/грейдам и найти те, что подходят лично вам для практики.
В течение 1-2 дней я объявлю о краудфандинг кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки и смогут попасть на закрытое бета-тестирование. А первые 150 донатеров получать особо-выгодную цену и бонус.
🚀 Следите за стартом 👉 в этом телеграм канале, в нем информация о старте будет опубликована за 6 часов до официального начала.
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
В Android используются разные единицы измерения для работы с экраном, чтобы адаптировать UI под разные устройства.
px (pixel) – это самая маленькая единица изображения на экране. Не зависит от плотности экрана → на разных устройствах элементы могут выглядеть слишком маленькими или большими.
<TextView
android:layout_width="100px"
android:layout_height="50px"/>
dp (density-independent pixel) – это абстрактная единица измерения, которая адаптируется под плотность экрана (dpi). Рекомендуется для всех размеров UI, чтобы интерфейс выглядел одинаково на разных устройствах.
<TextView
android:layout_width="100dp"
android:layout_height="50dp"/>
Пример в Kotlin
val sizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics
)
sp (scale-independent pixel) – как dp, но ещё учитывает настройки шрифта пользователя. Используется только для размеров шрифтов. <TextView
android:text="Привет, мир!"
android:textSize="16sp"/>
Пример в Kotlin
val textSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics
)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Да, есть три основных случая:
1. Блокирующий код – если внутри корутины используется блокирующая операция (Thread.sleep(), while(true) {}), она не реагирует на отмену.
2. Отмена родительской корутины не отменяет launch(NonCancellable) – если корутина запущена с NonCancellable, она игнорирует отмену.
3. Отмена не срабатывает, если корутина не проверяет isActive или yield() – долгие вычисления без точек приостановки не дадут корутине завершиться.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
Если нужно собирать данные из нескольких RSS-источников, можно реализовать это так:
Загрузить данные из нескольких RSS-лент (через
Retrofit, Jsoup, XmlPullParser). Парсить XML и извлекать статьи.
Объединять данные в общий список (
List<Article>). Сортировать по дате.
Сохранять в БД (Room) для офлайн-доступа.
Показывать в
RecyclerView или LazyColumn (Compose). В
build.gradle.ktsdependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0") // HTTP-клиент
implementation("com.squareup.retrofit2:converter-simplexml:2.9.0") // XML-парсер
implementation("org.jsoup:jsoup:1.16.1") // Альтернативный XML-парсер
implementation("androidx.room:room-runtime:2.6.1") // Room для кеша
kapt("androidx.room:room-compiler:2.6.1")
}RSS-лента содержит заголовок, ссылку, дату и описание
@Root(name = "item", strict = false)
data class RssItem(
@field:Element(name = "title") var title: String = "",
@field:Element(name = "link") var link: String = "",
@field:Element(name = "pubDate", required = false) var pubDate: String = "",
@field:Element(name = "description", required = false) var description: String = ""
)
Создаём API для загрузки RSS (Retrofit + XML)
interface RssService {
@GET("rss.xml")
fun getFeed(): Call<RssFeed>
}
@Root(name = "rss", strict = false)
data class RssFeed(
@field:Element(name = "channel") var channel: RssChannel = RssChannel()
)
@Root(name = "channel", strict = false)
data class RssChannel(
@field:ElementList(entry = "item", inline = true) var items: List<RssItem> = listOf()
)Создадим
Repository, который загружает и объединяет данные: class RssRepository(private val services: List<RssService>) {
fun fetchAllFeeds(): List<RssItem> {
val allItems = mutableListOf<RssItem>()
services.forEach { service ->
val response = service.getFeed().execute()
if (response.isSuccessful) {
response.body()?.channel?.items?.let { allItems.addAll(it) }
}
}
return allItems.sortedByDescending { parseDate(it.pubDate) }
}
private fun parseDate(dateString: String): Long {
return SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH)
.parse(dateString)?.time ?: 0
}
}Создаём Entity для БД
@Entity
data class ArticleEntity(
@PrimaryKey val link: String,
val title: String,
val pubDate: Long,
val description: String
)
Создаём DAO
@Dao
interface RssDao {
@Query("SELECT * FROM ArticleEntity ORDER BY pubDate DESC")
fun getArticles(): LiveData<List<ArticleEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertArticles(articles: List<ArticleEntity>)
}
Сохраняем в БД после загрузки
class RssRepository(private val dao: RssDao, private val services: List<RssService>) {
fun fetchAndCacheFeeds() {
val articles = fetchAllFeeds().map {
ArticleEntity(it.link, it.title, parseDate(it.pubDate), it.description)
}
dao.insertArticles(articles)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Forwarded from easyoffer
🎉 Краудфандинг easyoffer 2.0 стартовал!
Друзья, с этого момента вы можете поддержать проект и получить существенный бонус:
🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
➕ Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)
Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer
📌 Если не получается оплатить через карту РФ — напишите мне @kivaiko, и мы найдём удобный способ
Друзья, с этого момента вы можете поддержать проект и получить существенный бонус:
🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
➕ Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)
Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer
📌 Если не получается оплатить через карту РФ — напишите мне @kivaiko, и мы найдём удобный способ
Forwarded from easyoffer
Я поставил целью сбора скромные 300 тыс. рублей, но ребята, вы накидали больше млн. всего за 1 день. Это просто невероятно!
Благодаря вашей поддержке, я смогу привлечь еще больше людей для разработки сайта и обработки собеседований. Ваш вклад сделает проект качественнее и ускорит его выход! Огромное вам спасибо!
Краудфандинг будет продолжаться еще 31 день и все кто поддержать проект сейчас, до его выхода, смогут получить:
🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
➕ Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)
Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer
Огромное спасибо за вашу поддержку! 🤝
Благодаря вашей поддержке, я смогу привлечь еще больше людей для разработки сайта и обработки собеседований. Ваш вклад сделает проект качественнее и ускорит его выход! Огромное вам спасибо!
Краудфандинг будет продолжаться еще 31 день и все кто поддержать проект сейчас, до его выхода, смогут получить:
🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
➕ Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)
Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer
Огромное спасибо за вашу поддержку! 🤝
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Код для работы с SQLite в Android не генерируется автоматически — разработчик сам пишет SQL-запросы и управляет БД.
Но если используется Room (ORM для SQLite), то код генерируется на этапе компиляции с помощью Annotation Processing (
kapt) или KSP. Если используешь
SQLiteOpenHelper, то код не генерируется, ты сам пишешь SQL-запросы: class MyDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "mydb", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS users")
onCreate(db)
}
}Если используешь Room, код автоматически генерируется во время компиляции с помощью Annotation Processing (
kapt) или KSP. Ты пишешь аннотации (
@Entity, @Dao, @Database). Room-аннотации анализируются при компиляции (
kapt / KSP). Генерируется SQL-код и DAO-методы.
@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAllUsers(): List<User>
@Insert
fun insert(user: User)
}
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Если используешь
kapt (Annotation Processor) или KSP, то код генерируется во время компиляции. dependencies {
implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1") // Кодогенерация
}Пример с
KSP (быстрее kapt) dependencies {
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1") // Кодогенерация
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это внутренний компонент Compose, отвечающий за управление состоянием и рендерингом. Через него Compose отслеживает изменения в UI и обновляет только те элементы, которые изменились. Он также управляет процессом "recomposition".
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
Класс-наследник в Kotlin получает доступ к функционалу родительского класса через:
Наследование (
:) — доступ ко всем open методам и свойствам. Ключевое слово
super — вызов методов родителя. Переопределение (
override) — изменение поведения. Класс-наследник получает доступ ко всем
open методам и свойствам родителя. open class Animal(val name: String) {
fun eat() {
println("$name ест")
}
}
class Dog(name: String) : Animal(name)
fun main() {
val dog = Dog("Шарик")
dog.eat() // Шарик ест (метод унаследован)
}Пример
open class Animal {
open fun makeSound() {
println("Животное издаёт звук")
}
}
class Dog : Animal() {
override fun makeSound() {
super.makeSound() // Вызываем метод родителя
println("Гав-гав!")
}
}
fun main() {
val dog = Dog()
dog.makeSound()
}Вывод
Животное издаёт звук
Гав-гав!
Класс-наследник может изменить поведение родительского метода.
open class Animal {
open fun move() {
println("Животное двигается")
}
}
class Bird : Animal() {
override fun move() {
println("Птица летит") // Переопределяем метод
}
}
fun main() {
val bird = Bird()
bird.move() // Птица летит
}Пример
open class Animal(val name: String)
class Dog(name: String, val breed: String) : Animal(name) {
fun info() {
println("Имя: $name, Порода: $breed") // Используем `name` из родителя
}
}
fun main() {
val dog = Dog("Бобик", "Лабрадор")
dog.info() // Имя: Бобик, Порода: Лабрадор
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥2😁1
В Java есть 4 модификатора доступа, которые контролируют видимость полей, методов и классов:
Поля почти всегда должны быть
private, чтобы инкапсулировать данные. Методы тоже лучше делать private, если они не должны использоваться извне. public class User {
private String name; // ✅ Доступен только внутри класса
public User(String name) {
this.name = name;
}
private void logUserAction() { // ✅ Только внутри класса
System.out.println(name + " совершил действие");
}
}Используется, если класс не должен быть доступен за пределами пакета. Полезно для вспомогательных классов.
class FileHelper { // ✅ Доступен только в этом пакете
static void readFile(String path) {
System.out.println("Читаем файл: " + path);
}
}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 class UserManager { // ✅ Доступен везде
public void createUser() { // ✅ Можно вызывать в любом месте
System.out.println("Создание пользователя...");
}
}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
👍3
Это аннотация в Compose, которая указывает, что объект стабилен и его свойства не изменяются спонтанно. Это помогает Compose эффективно определять, когда нужно перерисовывать UI, уменьшая количество лишних перерисовок.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
👍5
Это асинхронный поток данных, который работает как List, но лениво.
Особенности:
- Выдает значения последовательно – одно за другим.
- Не блокирует поток – работает с suspend-функциями.
- Поддерживает cancel() – может быть остановлен в любой момент.
- Позволяет работать с бесконечными потоками – полезно для сетевых запросов, БД, UI-событий.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤1