0Pricing
Kotlin Academy · Lekcja

Projekcja gwiazdkowa i kiedy używać *

Stosuj projekcję gwiazdkową dla nieznanych typów generycznych i poznaj jej ograniczenia.

Projekcja gwiazdkowa i kiedy używać * to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Czym jest projekcja gwiazdkowa?

Projekcja gwiazdkowa (*) to sposób Kotlina na określenie „typu generycznego z nieznanym argumentem typu”. Jest bezpiecznym odpowiednikiem surowego typu lub nieograniczonego wildcarda <?> w Javie.

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"))
}

Odczyt z typów z projekcją gwiazdkową

Z obiektu List<*> można odczytywać elementy jako Any?. Nie można dodawać elementów, ponieważ rzeczywisty typ jest nieznany.

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))
}

Gwiazda a out Any

List<*> i List<out Any?> są równoważne. Projekcja gwiazdkowa jest skróconym zapisem projekcji kowariantnej z górnym ograniczeniem.

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
}

Gwiazda w parametrach funkcji

Użyj * w sygnaturach funkcji, gdy trzeba akceptować dowolny wariant generyczny, ale wywoływać tylko metody, które nie wymagają znajomości 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))
}

Gwiazda z MutableList

W przypadku MutableList<*> można odczytywać elementy jako Any?, ale nie można niczego zapisywać — kompilator na to nie pozwala.

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]
}

Klasa z projekcją gwiazdkową

Podczas sprawdzania typu klasy generycznej w czasie działania użyj projekcji gwiazdkowej, ponieważ argumenty typów są wymazywane.

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
}

Wiele parametrów z projekcją gwiazdkową

Funkcja może mieć wiele parametrów z projekcją gwiazdkową podczas pracy z parami lub mapami o nieznanych typach.

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))
}

Kiedy NIE używać gwiazdy

Unikaj projekcji gwiazdkowej, gdy trzeba zapisywać do kolekcji lub gdy zamiast niej można użyć ograniczonego parametru typu — ograniczony parametr dostarcza więcej informacji.

// 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)
}

Gwiazda przy sprawdzaniu typów i rzutowaniu

Użyj projekcji gwiazdkowej do wykonywania sprawdzeń instanceof dla klas generycznych, a następnie rzutuj na konkretny typ, aby wykonać właściwe operacje.

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)
}

Praktyka: generyczny dyspozytor zdarzeń

Dyspozytor, który akceptuje dowolny obiekt Handler<*>, ale bezpiecznie obsługuje zdarzenia o określonych typach.

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)
}

Gwiazda w kontekstach reified

Projekcja gwiazdkowa jest szczególnie często używana w kodzie refleksji i serializacji, gdzie typ w czasie działania jest sprawdzany za pomocą KClass<*>.

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

Szybkie sprawdzenie

Jaki typ otrzymuje się podczas odczytu z obiektu List<*>?

Podsumowanie

Projekcja gwiazdkowa (*) akceptuje dowolny wariant generyczny w dostępie tylko do odczytu. Używaj jej do sprawdzania typów, funkcji generycznych i refleksji. Unikaj jej, gdy trzeba zapisywać do kolekcji lub gdy ograniczony parametr typu zapewnia lepsze bezpieczeństwo typów.

Często zadawane pytania

Czy lekcja „Projekcja gwiazdkowa i kiedy używać *” jest bezpłatna?

Tak — pełny tekst „Projekcja gwiazdkowa i kiedy używać *” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Projekcja gwiazdkowa i kiedy używać *”?

Stosuj projekcję gwiazdkową dla nieznanych typów generycznych i poznaj jej ograniczenia. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Projekcja gwiazdkowa i kiedy używać *”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Funkcje generyczne i ograniczenia typów za pomocą where
  2. Wariancja deklarowana w miejscu deklaracji: in i out
  3. Projekcja gwiazdkowa i kiedy używać *
  4. Wymazywanie typów i parametry typów reified
← Powrót do Kotlin Academy