Star Projection
In this page:
fun functionName(value: Any) {
if (value is List<*>) { // any element type
// can read elements as Any?
}
}
fun read(items: List<*>) { }
Using Star Projection in Type Checks
Because generic type arguments are erased at runtime, checking whether something is a List of unknown element type uses the star projection syntax List<*>.
Example: Using Star Projection in Type Checks
// Define the function `printSize` taking `value`
fun printSize(value: Any) {
// Check whether `value is List<*>`
if (value is List<*>) {
// Print "List size: ${value.size}" to the console, with a trailing newline
println("List size: ${value.size}")
}
}
// Entry point: execution of the program starts here
fun main() {
printSize(listOf(1, 2, 3))
printSize(listOf("a", "b"))
}
Login to try C/C++/Java/PHP code in the editor
Reading from a Star-Projected Type
A star-projected generic type still allows reading elements out safely, since any element can always be treated as Any? regardless of its real type.
Example: Reading from a Star-Projected Type
// Define the function `printAll` taking `items`
fun printAll(items: List<*>) {
// Loop over `items`, binding each item to `item`
for (item in items) {
// Print `item` to the console, with a trailing newline
println(item)
}
}
// Entry point: execution of the program starts here
fun main() {
printAll(listOf(1, "two", 3.0))
}
Login to try C/C++/Java/PHP code in the editor
Why Writing Is Disallowed
You cannot add elements to a star-projected MutableList<*> because the compiler has no way to guarantee that whatever you try to insert matches the list's real, unknown element type.
Example: Why Writing Is Disallowed
fun describeMutable(list: MutableList<*>) {
// list.add("something") would not compile here
println("A mutable list with ${list.size} elements of an unknown type")
}
fun main() {
val numbers = mutableListOf(1, 2, 3)
describeMutable(numbers)
}
Login to try C/C++/Java/PHP code in the editor
Star Projection vs a Concrete Type
Whenever the actual element type is known and relevant, prefer a concrete generic type like List<String> over List<*>, since it lets the compiler check far more of your code for correctness.
Example: Star Projection vs a Concrete Type
// Define the function `sumConcrete` taking `numbers` and returning an Int
fun sumConcrete(numbers: List<Int>): Int = numbers.sum()
// Entry point: execution of the program starts here
fun main() {
// Print `sumConcrete(listOf(1, 2, 3, 4))` to the console, with a trailing newline
println(sumConcrete(listOf(1, 2, 3, 4)))
}
Login to try C/C++/Java/PHP code in the editor
- Trying to add an element to a
MutableList<*>, which the compiler rejects since the actual element type is unknown, so nothing can be safely inserted. - Confusing
List<*>withList<Any?>; a star projection is more restrictive because it also blocks unsafe writes, whileList<Any?>allows adding any value. - Overusing star projection when the actual type parameter is already known and a concrete generic type would be clearer and safer.
Type<*>(a star projection) represents a generic type when the exact type argument is unknown or unimportant.- A star-projected generic type allows safe reading (of a supertype like
Any?) but disallows writing, since the compiler cannot verify type safety for insertion. - Star projection is commonly used in
ischecks, likeis List<*>, since erasure prevents checking the actual element type. List<*>behaves like a read-only list of unknown element type, distinct fromList<Any?>which explicitly allows any type.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: