Question
In Kotlin generics, what is the difference between SomeGeneric<*> and SomeGeneric<Any>?
I understand * as a wildcard that can represent any type, and Any as the root type that all non-null objects inherit from. Because of that, they seem similar at first glance. Are they actually the same, or do they behave differently?
For example:
class SomeGeneric<T>(val value: T)
How should I think about these two types?
SomeGeneric<*>
SomeGeneric<Any>
Short Answer
By the end of this page, you will understand why SomeGeneric<*> and SomeGeneric<Any> are not the same in Kotlin. You will learn what star projection means, how it differs from explicitly using Any, and how variance affects what you can safely read from or write to a generic type.
Concept
In Kotlin, SomeGeneric<Any> means a generic type whose type argument is exactly Any.
SomeGeneric<*> means a generic type whose type argument is unknown.
That difference is very important.
SomeGeneric<Any>
When you write:
SomeGeneric<Any>
you are saying:
- this generic container holds values of type
Any - the type parameter is known and fixed as
Any - this is not the same as “any possible generic type argument”
For example, if you have SomeGeneric<String>, that is usually not assignable to SomeGeneric<Any> because generic types in Kotlin are invariant by default.
SomeGeneric<*>
When you write:
SomeGeneric<*>
you are saying:
- this is a
SomeGenericof , but I do not know which one
Mental Model
Imagine labeled boxes.
SomeGeneric<Any>is a box labeled: "This box storesAnyvalues."SomeGeneric<*>is a box labeled: "This box stores some type, but the label is hidden."
If the label says Any, you know what kind of values are allowed.
If the label is hidden, you can look at the box carefully, but you cannot safely put just any value into it, because the real hidden label might actually be String, Int, or something else.
So:
Anyis a known type*is an unknown type argument
That is the mental shift that makes Kotlin generics easier to understand.
Syntax and Examples
Here is a simple generic class:
class Box<T>(var value: T)
Example 1: Box<Any>
val box: Box<Any> = Box("hello")
box.value = 42
box.value = true
This works because Box<Any> can store any non-null value.
Example 2: Box<String> is not Box<Any>
val stringBox: Box<String> = Box("hello")
// Does not compile
// val anyBox: Box<Any> = stringBox
Why not? Because if Kotlin allowed this, you could do:
val stringBox: Box<String> = Box("hello")
// val anyBox: Box<Any> = stringBox
// anyBox.value = 123
Now a Box<String> would contain an , which breaks type safety.
Step by Step Execution
Consider this code:
class Box<T>(var value: T)
fun main() {
val original: Box<String> = Box("Kotlin")
val unknown: Box<*> = original
val result = unknown.value
println(result)
}
Step 1: Create original
val original: Box<String> = Box("Kotlin")
originalis aBox<String>- its
valueis the string"Kotlin"
Step 2: Assign to Box<*>
val unknown: Box<*> = original
- this is allowed
- Kotlin treats
unknownas “aBoxof some unknown type”
Real World Use Cases
Star projections are useful when you need to work with generic objects but do not care about their exact type parameter.
1. Logging and debugging
You may want to print or inspect a generic container without needing to know its exact type.
fun printBox(box: Box<*>) {
println(box.value)
}
2. Framework and library code
Libraries often receive values whose generic types are not known in advance.
Examples:
- serializers
- dependency injection containers
- reflection utilities
- UI binding code
3. Working with collections of unknown element type
fun printList(list: List<*>) {
for (item in list) {
println(item)
}
}
This is common when writing reusable helper functions.
4. API boundaries
Sometimes an API only needs to know that a generic object exists, not what specific type it contains.
In that case, * is often better than forcing Any.
5. Safer read-only access
Real Codebase Usage
In real projects, developers use * and Any for different purposes.
Use SomeGeneric<*> when the type argument is unknown
This is common in utility code.
fun logValue(box: Box<*>) {
println("Value: ${box.value}")
}
Pattern:
- inspect values
- pass generic objects around without caring about the exact type
- avoid unsafe casts
Use SomeGeneric<Any> when the container truly stores Any
val settings: Box<Any> = Box("dark")
settings.value = 10
settings.value = false
Pattern:
- mixed-type storage
- configuration maps
- simple dynamic containers
Guard clauses and safe checks
When you receive a star-projected type, you often combine it with type checks:
{
value = box.value
(value ! String)
println(value.uppercase())
}
Common Mistakes
1. Thinking * means the same as Any
This is the most common mistake.
Any= a specific known type*= an unknown type argument
Broken assumption:
class Box<T>(var value: T)
val stringBox: Box<String> = Box("hello")
// val anyBox: Box<Any> = stringBox
This does not compile.
2. Forgetting that generics are invariant by default
val strings: MutableList<String> = mutableListOf("a", "b")
// val items: MutableList<Any> = strings
This is unsafe, so Kotlin rejects it.
How to avoid it:
- learn invariance, covariance, and contravariance
- use
List<*>,List<out Any>, or a properly variant type when appropriate
3. Trying to write into a star-projected mutable generic
Comparisons
| Type | Meaning | Can represent Box<String>? | Can safely write arbitrary Any? | Typical use |
|---|---|---|---|---|
Box<Any> | A box whose type argument is exactly Any | No | Yes | Mixed-type container |
Box<Any?> | A box whose type argument is exactly Any? | No | Yes, including null | Mixed-type container that allows null |
Box<*> | A box of unknown type argument | Yes | No |
Cheat Sheet
Any= root type for all non-null objects in KotlinAny?= root type includingnullSomeGeneric<Any>= generic type with the exact type argumentAnySomeGeneric<*>= generic type with an unknown type argument
Key rule
SomeGeneric<*> is not the same as SomeGeneric<Any>.
Use SomeGeneric<Any> when
- you truly want the container to store
Any - you need to write different non-null types into it
Use SomeGeneric<*> when
- you do not know the type argument
- you only need safe access without assuming a specific type
Safety intuition
- known type (
Any) -> more operations allowed - unknown type (
*) -> fewer operations allowed
Common examples
FAQ
Is * just a wildcard version of Any in Kotlin?
No. * means the type argument is unknown. Any is a specific concrete type.
Why can't Box<String> be assigned to Box<Any>?
Because Box is invariant by default. Allowing that assignment could let you put a non-String value into a Box<String>.
When should I use Box<*>?
Use it when you need to accept a generic type without caring what its type argument is, especially for reading, logging, or inspection.
Can I write values into Box<*>?
Usually no, because the real type argument is unknown.
Is Any the same as Java's Object?
Roughly yes as the root non-null type, but Kotlin's type system also separates nullable and non-null types.
What is star projection in Kotlin?
Star projection is Kotlin's way of saying “this generic type has some type argument, but we do not know which one.”
Mini Project
Description
Build a small Kotlin program that inspects generic boxes without needing to know their exact type parameter. This demonstrates when Box<*> is useful and why it is safer than assuming Box<Any>.
Goal
Create a reusable function that accepts boxes of different types, prints their values, and safely handles type-specific logic.
Requirements
- Create a generic
Box<T>class. - Create at least three boxes with different type arguments such as
String,Int, andBoolean. - Write a function that accepts
Box<*>and prints the value. - Add a type check so strings are handled differently from other values.
- Show that
Box<String>can be passed toBox<*>logic, but not toBox<Any>.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.