skip to content

For a value of type Array<out Number>, which Array members can you call and which are forbidden, and why?

level: middleimportance: must knowfreq 45%

answer

  1. out → get OK, set blocked
  2. set's value projected to Nothing
  3. size/iterator always fine (no T)
  4. in → set OK, get returns Any?
  5. compiler checks in/out position of T

basics

~10 s

You can read: get returns Number, and size works. You cannot call set, because the real element type is unknown and writing a Number could be wrong. Out makes it read-only for elements.

solid answer

~40 s

With Array<out Number>, the type parameter is projected covariantly, so only members where T appears in an out (return) position stay callable with their normal types. get(index): Number works, size and indices work, iterator() works. Members where T is in an in (parameter) position become restricted: set(index, value: T) is effectively typed as set(index, value: Nothing), so you can never supply a real argument and the compiler rejects any set call. The reason: the actual array might be Array<Int>; allowing set(i, someNumber) could store a non-Int into an Int array. The compiler enforces this statically, not at runtime. fill(fromIndex, toIndex, element: T) is similarly blocked because element is an in-position. So Array<out Number> is a read-only-elements view even though the underlying object is fully mutable.

code

kotlin · 8 lines
kotlin
fun sumFirst(a: Array<out Number>): Double {
    val first: Number = a[0]   // get allowed
    // a[0] = 0.0              // compile error: value projected to Nothing
    return first.toDouble()
}

sumFirst(arrayOf(1, 2, 3))          // Array<Int> ok
sumFirst(arrayOf(1.5, 2.5))         // Array<Double> ok

go deeper

for a junior

Knows out allows reads and blocks writes on the array.

for a middle

Maps in/out positions of T to which members survive and names get vs set precisely.

for a senior

Explains the value parameter is projected to Nothing and why that statically blocks set.

for a principal

Articulates the soundness model: projection restricts the API surface to the variance-safe subset to preserve type safety on a mutable type.

## How projection maps to member availability Kotlin decides which members survive a projection by looking at **where the type parameter `T` appears** in each member's signature: - **out position** (return type): safe under `out` projection — kept as-is. - **in position** (parameter type): unsafe under `out` projection — the parameter is projected to `Nothing`, making the member effectively uncallable. For `Array<T>` the relevant members are: ```kotlin operator fun get(index: Int): T // T is OUT (return) operator fun set(index: Int, value: T) // T is IN (parameter) val size: Int // no T ``` ## Under Array<out Number> ```kotlin fun inspect(a: Array<out Number>) { val n: Number = a[0] // OK: get(): Number val s = a.size // OK: no T involved for (x in a) println(x) // OK: iterator/get // a[0] = 1 // ERROR: set's value is projected to Nothing } ``` `a[0]` returns `Number` — perfectly usable. But `a[0] = 1` fails to compile. The compiler treats `set` as if its signature were `set(index: Int, value: Nothing)`, and **nothing except `Nothing` itself** (which has no instances) can be passed, so every `set` call is rejected. ## Why `Nothing`? `Nothing` is Kotlin's bottom type — a subtype of every type with no values. Projecting the consumed `T` to `Nothing` is the formal way the compiler says "there is no value you could safely write here", because the real element type is some unknown subtype of `Number`. ## Contrast with Array<in Number> The mirror image: `set(index, value)` accepts `Number` (and subtypes like `Int`), but `get` is projected so its return type becomes `Any?` — you lose the precise read type. So `out` = read precisely / no write; `in` = write `Number` / read only as `Any?`. ## Practical takeaway `Array<out Number>` is the idiomatic way to declare a parameter you only read element-wise. It documents intent and lets callers pass `Array<Int>`, `Array<Double>`, etc.

  • What exactly is the inferred type of the value parameter of set under Array<out Number>?
    It is projected to Nothing. Since Nothing has no instances, no argument satisfies it, so set is uncallable.
  • Does the projection change runtime behavior of the array object?
    No. The underlying array is still fully mutable; projection is a compile-time view restriction only.

saying these in an interview costs you the question

  • Saying set just throws at runtime instead of being a compile error
  • Claiming size becomes unavailable under projection
  • Thinking get returns Any? under out (it returns Number)
  • Not mentioning Nothing as the projected parameter type
  • Believing the array becomes immutable for everyone, not just through this view

context