Звёздная проекция и случаи применения *
Применяйте звёздную проекцию для неизвестных обобщённых типов и разбирайтесь в её ограничениях.
«Звёздная проекция и случаи применения *» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Обобщённые функции и ограничения типов с where
- Вариативность на месте объявления: in и out
- Звёздная проекция и случаи применения *
- Стирание типов и параметры типов reified