skip to content

Explain Array<in T> (a use-site 'in' projection): when would you use it, and what does it do to get and set?

level: middleimportance: should knowfreq 35%

answer

  1. in = consumer/write, accepts supertype arrays
  2. set keeps T, get widens to Any?
  3. fillWithZeros(dest: Array<in Int>)
  4. in mirrors out exactly
  5. use for write-only sinks/buffers

basics

~20 s

Array<in T> means 'I only write T into this array'. You can call set with a T, and you can pass arrays of supertypes. But reading gives back Any?, because the array may actually hold a broader type.

solid answer

~40 s

An in-projection makes the type parameter contravariant at the use site, turning the array into a consumer of T. set(index, value: T) stays callable because value is in an in-position. In exchange, get loses precision: its return type is projected to Any? (the supertype of all the unknown possible element types). The use case is a sink/destination you only write to. For example fun fillWithZeros(dest: Array<in Int>): you write Ints, and you can pass an Array<Int>, Array<Number>, or Array<Any> because all of them can safely hold an Int. This is the contravariant counterpart of Array<out T>; together they correspond to Java wildcards but expressed with in/out. The key trade-off: in gives you write-with-T but read-as-Any?, while out gives read-as-T but no write.

code

kotlin · 6 lines
kotlin
fun <T> addAll(dest: Array<in T>, src: Array<out T>) {
    for (i in src.indices) dest[i] = src[i]  // read T from src, write T to dest
}

val target: Array<Any> = arrayOf<Any>(0, 0)
addAll(target, arrayOf("a", "b"))  // dest: Array<in String> via Array<Any>

go deeper

for a junior

Recognizes in means writing and that supertype arrays can be passed.

for a middle

States set stays usable and get widens to Any?, with a destination/sink use case.

for a senior

Explains why Any? is the only sound read type and pairs in/out in a copy signature.

for a principal

Frames in/out projections as the use-site encoding of the producer/consumer split, preserving soundness on a mutable invariant type.

## What an in-projection is `Array<in T>` applies **contravariant** variance at the use site. It says "this array is a **consumer** of `T` — I will write `T`-typed values into it". Where `out` was about producing/reading, `in` is about consuming/writing. ## Subtyping it enables With `dest: Array<in Int>`, you may pass any array whose element type is `Int` **or a supertype** of `Int`: ```kotlin fun fillWithZeros(dest: Array<in Int>) { for (i in dest.indices) dest[i] = 0 // set(value: Int) is allowed } fillWithZeros(arrayOf(1, 2, 3)) // Array<Int> fillWithZeros(arrayOf<Number>(1, 2, 3)) // Array<Number> fillWithZeros(arrayOf<Any>(1, 2, 3)) // Array<Any> ``` All are safe destinations for an `Int`, because an `Int` is a `Number` and an `Any`. ## Effect on members Kotlin again looks at the position of `T`: - `set(index: Int, value: T)` — `T` is in an **in-position** → **kept**; you can pass an `Int`. - `get(index: Int): T` — `T` is in an **out-position** → **projected**; its return type becomes `Any?`. ```kotlin fun demo(dest: Array<in Int>) { dest[0] = 42 // OK: set val x = dest[0] // x: Any? (precise type lost) } ``` ## Why `get` returns `Any?` The real array might be `Array<Any>`, so an element could be any object. The only type guaranteed to describe every possible element is `Any?` (nullable `Any`, the top type). The compiler therefore widens the read result to `Any?`. ## Mirror summary | Projection | get returns | set accepts | Accepts arrays of | |---|---|---|---| | `Array<out T>` | `T` | nothing (`Nothing`) | `T` and subtypes | | `Array<in T>` | `Any?` | `T` (and subtypes) | `T` and supertypes | ## When to use it Use `Array<in T>` for a **write-only destination** parameter — a buffer you fill, a sink you push into. Use `Array<out T>` for a **read-only source**. This is the use-site, per-parameter way to express the producer/consumer split.

  • Why does get return Any? rather than T under an in-projection?
    Because the underlying array's element type is some unknown supertype of T, so the only sound static type for a read is the top type Any?.

Array<in T> is a mailbox slot: you can drop T-shaped letters in (set), but if you peek inside you only know it's 'some object' (Any?).

saying these in an interview costs you the question

  • Saying in lets you read elements as T
  • Claiming in accepts subtype arrays (it accepts supertypes)
  • Forgetting that set stays callable under in
  • Confusing in-projection with declaration-site contravariance on the class
  • Saying get is forbidden entirely under in (it returns Any?, not blocked)

context