0Pricing
Kotlin Academy · Урок

Звёздная проекция и случаи применения *

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

«Звёздная проекция и случаи применения *» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Что такое проекция со звёздочкой

Проекция со звёздочкой (*) — это способ Kotlin сказать: «обобщённый тип с неизвестным аргументом типа». Это безопасный эквивалент необобщённого типа Java или неограниченного шаблона <?>.

fun printList(list: List<*>) {
    for (item in list) println(item)
}
fun main() {
    printList(listOf(1, 2, 3))
    printList(listOf("a", "b"))
    printList(listOf(true, 42, "mixed"))
}

Чтение из типов с проекцией со звёздочкой

Из List<*> можно читать элементы как Any?. Добавлять элементы нельзя, потому что фактический тип неизвестен.

fun process(list: List<*>) {
    val first: Any? = list.firstOrNull()
    println("First: $first, Size: ${list.size}")
    // list.add("x") // compile error: can't write to List<*>
}
fun main() {
    process(listOf(10, 20, 30))
}

Звёздочка и out Any

List<*> и List<out Any?> эквивалентны. Проекция со звёздочкой — это сокращённая запись ковариантной проекции с верхней границей.

fun sumStars(list: List<*>): Int = list.size
fun sumOut(list: List<out Any?>): Int = list.size
fun main() {
    val l = listOf(1, "two", true)
    println(sumStars(l))  // 3
    println(sumOut(l))    // 3
}

Звёздочка в параметрах функций

Используйте * в сигнатурах функций, когда нужно принимать любой вариант обобщённого типа, но вызывать только методы, которым не нужно знать T.

fun containerInfo(map: Map<*, *>): String {
    return "keys=${map.keys.size}, values=${map.values.size}"
}
fun main() {
    val m = mapOf(1 to "one", 2 to "two")
    println(containerInfo(m))
}

Звёздочка с MutableList

Для MutableList<*> можно читать значения как Any?, но нельзя ничего записывать — компилятор это запрещает.

fun clearFirstElement(list: MutableList<*>) {
    if (list.isNotEmpty()) {
        list.removeAt(0)  // OK: remove by index doesn't need T
        // list.add("x") // Error: can't write unknown type
    }
}
fun main() {
    val m = mutableListOf(1, 2, 3)
    clearFirstElement(m)
    println(m) // [2, 3]
}

Класс с проекцией со звёздочкой

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

fun isListOfSomething(obj: Any): Boolean {
    return obj is List<*>  // OK with star
    // obj is List<String> // Warning: cannot check erased type
}
fun main() {
    println(isListOfSomething(listOf(1, 2)))  // true
    println(isListOfSomething("not a list"))  // false
}

Несколько параметров со звёздочной проекцией

Функция может иметь несколько параметров со звёздочной проекцией при работе с парами или картами неизвестных типов.

fun describeMap(map: Map<*, *>) {
    map.forEach { (k, v) ->
        println("${k?.javaClass?.simpleName} -> ${v?.javaClass?.simpleName}")
    }
}
fun main() {
    describeMap(mapOf("key" to 42, 1 to true))
}

Когда НЕ следует использовать звёздочку

Избегайте проекции со звёздочкой, когда нужно записывать данные в коллекцию или когда вместо неё можно использовать ограниченный параметр типа: ограниченный параметр даёт больше информации.

// Bad: loses type information
fun addToStar(list: MutableList<*>, item: Any?) {
    // list.add(item) // compile error
}
// Better: use bounded type parameter
fun <T : Any> addTyped(list: MutableList<T>, item: T) {
    list.add(item)
}
fun main() {
    val l = mutableListOf("a")
    addTyped(l, "b")
    println(l)
}

Звёздочка при проверке типов и приведении

Используйте проекцию со звёздочкой для выполнения проверок обобщённых классов через instanceof, а затем приводите тип к конкретному для дальнейшей работы.

fun handleResponse(response: Any) {
    if (response is List<*>) {
        val list = response as List<*>
        println("List with ${list.size} elements")
        val first = list.firstOrNull()
        if (first is String) println("First string: $first")
    }
}
fun main() {
    handleResponse(listOf("hello", "world"))
    handleResponse(42)
}

Практика: диспетчер обобщённых событий

Диспетчер принимает любой Handler<*>, но безопасно отправляет события с определёнными типами.

interface Handler<in T> { fun handle(event: T) }
class LogHandler : Handler<Any> {
    override fun handle(event: Any) = println("Event: $event")
}
fun dispatch(handler: Handler<*>, event: Any) {
    @Suppress("UNCHECKED_CAST")
    (handler as Handler<Any>).handle(event)
}
fun main() {
    dispatch(LogHandler(), "click")
    dispatch(LogHandler(), 42)
}

Звёздочка в контекстах reified

Проекция со звёздочкой особенно часто встречается в коде отражения и сериализации, где тип во время выполнения проверяется через KClass<*>.

fun printClassName(klass: kotlin.reflect.KClass<*>) {
    println(klass.simpleName)
}
fun main() {
    printClassName(String::class)     // String
    printClassName(List::class)       // List
    printClassName(Int::class)        // Int
}

Быстрая проверка

Какой тип получается при чтении из List<*>?

Итоги

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

Часто задаваемые вопросы

Урок «Звёздная проекция и случаи применения *» бесплатный?

Да — полный текст урока «Звёздная проекция и случаи применения *» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Звёздная проекция и случаи применения *»?

Применяйте звёздную проекцию для неизвестных обобщённых типов и разбирайтесь в её ограничениях. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Звёздная проекция и случаи применения *»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Обобщённые функции и ограничения типов с where
  2. Вариативность на месте объявления: in и out
  3. Звёздная проекция и случаи применения *
  4. Стирание типов и параметры типов reified
← Назад к Kotlin Academy