Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Generics в Kotlin/Java — мощный инструмент для type-safety
Generics — это механизм, который позволяет писать переиспользуемый, типобезопасный код для работы с разными типами данных без потери типизации.
Проблема без Generics
class Container {
private var value: Any? = null
fun set(v: Any?) {
value = v
}
fun get(): Any? = value
}
// Проблемы:
val container = Container()
container.set("Hello")
val result = container.get() // Any? — нужно приводить тип
val str = result as String // Небезопасное приведение!
val error = result as Int // Может быть ClassCastException в runtime
Решение с Generics
class Container<T> {
private var value: T? = null
fun set(v: T) {
value = v
}
fun get(): T? = value
}
// Правильное использование:
val stringContainer = Container<String>()
stringContainer.set("Hello")
val str = stringContainer.get() // String?, компилятор знает тип!
val intContainer = Container<Int>()
intContainer.set(42)
val num = intContainer.get() // Int?
// stringContainer.set(42) // ОШИБКА КОМПИЛЯЦИИ — тип не совпадает!
Основные преимущества
Type Safety — ошибки ловятся на compile time, а не runtime Переиспользование — один код работает с разными типами Читаемость — явно видно с какими типами работает код Производительность — нет нужды в type casting
Примеры в Android разработке
1. Repository паттерн
interface Repository<T> {
suspend fun getAll(): List<T>
suspend fun getById(id: String): T?
suspend fun save(item: T)
suspend fun delete(item: T)
}
class UserRepository(private val api: UserApi) : Repository<User> {
override suspend fun getAll(): List<User> = api.getUsers()
override suspend fun getById(id: String): User? = api.getUser(id)
override suspend fun save(item: User) = api.saveUser(item)
override suspend fun delete(item: User) = api.deleteUser(item.id)
}
class ProductRepository(private val db: ProductDao) : Repository<Product> {
override suspend fun getAll(): List<Product> = db.getAll()
override suspend fun getById(id: String): Product? = db.getById(id)
override suspend fun save(item: Product) = db.insert(item)
override suspend fun delete(item: Product) = db.delete(item)
}
2. Result тип (часто используется)
seal class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
fun handleResult() {
val userResult: Result<User> = fetchUser()
when (userResult) {
is Result.Success -> println(userResult.data.name)
is Result.Error -> println(userResult.exception.message)
is Result.Loading -> println("Loading...")
}
}
3. StateFlow и LiveData
class UserViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
// Типизирован на User
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user.asStateFlow()
// Типизирован на List<User>
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
}
4. Extension функции с Generics
inline fun <T> T.let(block: (T) -> Unit): T {
block(this)
return this
}
val user = User("John", 25)
.let { println("Created: $it") }
.let { it.name }
// Практичный пример
inline fun <T> Result<T>.onSuccess(action: (T) -> Unit): Result<T> {
if (this is Result.Success) {
action(data)
}
return this
}
fetchUser()
.onSuccess { user -> updateUI(user) }
.onSuccess { user -> saveToCache(user) }
Bounded Type Parameters
// T должен быть Number
fun <T : Number> sum(a: T, b: T): Double {
return a.toDouble() + b.toDouble()
}
sum(5, 10) // OK — Int extends Number
sum(3.14, 2.86) // OK — Double extends Number
// sum("a", "b") // ОШИБКА — String не Number
// Несколько ограничений
fun <T> process(item: T) where T : Comparable<T>, T : Serializable {
// item должен быть Comparable и Serializable одновременно
}
Variance — Ковариантность и Контравариантность
// Ковариантность (out) — только возвращаем T
class Producer<out T> {
fun produce(): T = ???
// fun consume(item: T) {} // НЕЛЬЗЯ!
}
val producer: Producer<Any> = Producer<String>() // OK
// Контравариантность (in) — только принимаем T
class Consumer<in T> {
fun consume(item: T) { }
// fun produce(): T = ??? // НЕЛЬЗЯ!
}
val consumer: Consumer<String> = Consumer<Any>() // OK
Generic DAO для Database
abstract class BaseDao<T> {
@Insert
abstract suspend fun insert(entity: T)
@Update
abstract suspend fun update(entity: T)
@Delete
abstract suspend fun delete(entity: T)
}
@Dao
interface UserDao : BaseDao<User> {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUserById(id: String): User?
}
@Dao
interface ProductDao : BaseDao<Product> {
@Query("SELECT * FROM products WHERE category = :category")
suspend fun getByCategory(category: String): List<Product>
}
Type Erasure — важная концепция
// В runtime generics информация стирается
val list: List<String> = listOf("a", "b")
list is List<String> // true
list is List<Int> // true (оба true!) — Type Erasure
// Решение: inline reified
inline fun <reified T> safeCast(obj: Any): T? = obj as? T
val result = safeCast<String>("hello") // Работает корректно
val result2 = safeCast<String>(123) // null (безопасно)
Когда использовать Generics
- Контейнеры и коллекции (List<T>, Set<T>, Map<K, V>)
- API классы (Repository<T>, Result<T>, LiveData<T>)
- Переиспользуемые утилиты и расширения
- Когда логика универсальна для всех типов
Когда НЕ использовать
- Если тип фиксирован — используй конкретный тип
- Не усложняй иерархию слишком вложенными generics
- Избегай raw types (без <> скобок)
Итог
Generics — это фундаментальный инструмент modern Java/Kotlin разработки. Они обеспечивают:
- Type Safety на compile time
- Переиспользование кода без потери типизации
- Лучшую архитектуру через полиморфизм
- Профессиональный код который легче поддерживать
Мастерство Generics показывает глубокое понимание языка и способность писать правильные абстракции.