← Назад к вопросам

В чем разница между Star-projection и Any?

2.0 Middle🔥 61 комментариев
#Kotlin основы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Star-projection (*) и Any в Kotlin

Хотя на первый взгляд * (star-projection) и Any могут показаться похожими, поскольку оба могут "принять любой тип", их семантика, использование и ограничения принципиально различны. Это одно из ключевых различий в системе типов Kotlin, особенно важное при работе с дженериками (обобщенными типами) и вариативностью.

Что такое Any?

Any — это корневой супертип всей иерархии классов в Kotlin. Все типы, включая примитивы (на уровне JVM они представлены соответствующими классами-обертками), явно или неявно наследуются от Any. Это явный, конкретный тип.

val list: List<Any> = listOf(1, "строка", 3.14, true)
// Мы можем добавить объект ЛЮБОГО типа, но тип коллекции ЧЕТКО ЗАФИКСИРОВАН: List<Any>
list.add(CustomObject()) // OK, если list mutable

Ключевые характеристики Any:

  • Это конкретный тип с информацией.
  • List<Any> — это коллекция, которая может содержать элементы любого типа, но тип элементов известен и зафиксирован как Any.
  • При извлечении элемента мы получаем тип Any и для работы с конкретным типом необходимо явное приведение (as String).
  • Не имеет прямого отношения к дженерикам, это просто самый общий тип в иерархии.

Что такое Star-projection (*)?

Star-projection — это синтаксис для работы с обобщенными типами, когда информация о конкретном типовом аргументе неизвестна, неважна или недоступна. Это способ сказать: "Я знаю, что это List (или другой дженерик), но я не знаю или не волнуюсь о том, какой именно T используется".

fun printSize(list: List<*>) {
    println(list.size) // Мы можем использовать методы, не зависящие от T
    // val item: T = list[0] // ОШИБКА: Тип T НЕИЗВЕСТЕН!
    val item: Any? = list[0] // Единственная безопасная верхняя граница для чтения - `Any?`
    // list.add(??? ) // ОШИБКА: Нельзя писать, так как тип аргумента неизвестен и небезопасен
}

Ключевые характеристики *:

  • Это не тип, а синтаксическая проекция (или отсутствие информации) о типовом аргументе дженерика.
  • Используется исключительно с обобщенными типами: Container<*>, MutableList<*>, Array<*>.
  • Чтение: При чтении из Foo<*> значение имеет тип, соответствующий upper bound (верхней границе) параметра T. По умолчанию это Any? (если граница не указана явно, как в class Box<T : Vehicle>).
  • Запись: В подавляющем большинстве случаев (кроме специально спроектированных контрвариантных случаев) запись в Foo<*> запрещена. Компилятор не может проверить безопасность типа.
  • MutableList<Any> и MutableList<*> — это разные типы! Первый может принимать Any, второй — ничего не может принимать.

Сравнительная таблица

КритерийAny (например, List<Any>)Star-projection (например, List<*>)
СущностьКонкретный тип.Отсутствие информации о типовом аргументе.
ИспользованиеС любыми типами.Только с параметризованными типами (дженериками).
Чтение элементовТип известен: Any.Тип неизвестен. Фактический тип — это upper bound параметра (по умолчанию Any?).
Запись элементовРазрешена, если коллекция mutable. Можно добавить любой объект, т.к. он всегда является подтипом Any.Запрещена (в инвариантных и ковариантных случаях). Компилятор не может гарантировать безопасность.
Пример сигнатурыfun process(items: List<Any>)fun printItems(items: List<*>)
Пример вызоваprocess(listOf(1, 2)) // Ошибка типа! List<Int> не является List<Any>.printItems(listOf(1, 2)) // OK! Мы "стираем" информацию о типе элементов.
Отношение к вариативностиНе связано напрямую.Прямой способ работать с дженериком, игнорируя его параметр, с определенными ограничениями на запись.

Практический пример несовместимости

Этот пример ярче всего показывает разницу:

val intList: MutableList<Int> = mutableListOf(1, 2, 3)
val anyList: MutableList<Any> = intList // КОМПИЛЯЦИОННАЯ ОШИБКА! Type mismatch.
// Если бы это было разрешено, мы смогли бы сделать:
// anyList.add("I'm a String") // Нарушили бы типобезопасность исходного intList!
val starList: MutableList<*> = intList // OK! Star-projection как раз для этого.
// starList.add(4) // КОМПИЛЯЦИОННАЯ ОШИБКА! Запись в star-projection запрещена, типобезопасность сохраняется.

Вывод: Any — это самый общий конкретный тип объекта. * (star-projection) — это отсутствие информации о типовом параметре дженерика. Они решают разные задачи: Any используется для работы с элементами как с экземплярами общего супертипа, а * — для написания кода, который может принимать дженерики с любым (или неизвестным) аргументом типа, соблюдая при этом типобезопасность благодаря ограничениям на запись.

В чем разница между Star-projection и Any? | PrepBro