Generics Workbook
Practice problems for Kotlin Generics: Variance Without the Wildcards. Each takes a minute or two. Write your own answer first, then click Show answer — nothing here is a trick question, just direct practice of the syntax from the lesson.
the basics
1. A generic function
Write firstOrNull that takes a List of any element type and returns its first element, or null when empty.
Show answer Hide answer
fun <T> firstOrNull(list: List<T>): T? =
if (list.isEmpty()) null else list[0] 2. A generic class
Declare a Box that holds one value of any type.
Show answer Hide answer
class Box<T>(val value: T) 3. Inference at the call site
Given the Box above, create one holding "hello". Do you write the type argument?
Show answer Hide answer
val b = Box("hello") // T inferred as StringNo — the compiler infers T from the argument.
variance
4. A producer
Declare an interface Source that only ever produces a T (a next(): T method), marked so that a Source of Dog is usable as a Source of Animal.
Show answer Hide answer
interface Source<out T> {
fun next(): T
}out makes it covariant — Kotlin’s declaration-site version of Java’s ? extends.
5. A consumer
Declare an interface Sink that only ever consumes a T (an accept(value: T) method), marked so that a Sink of Animal is usable as a Sink of Dog.
Show answer Hide answer
interface Sink<in T> {
fun accept(value: T)
}in makes it contravariant — Kotlin’s version of ? super.
6. Covariant by declaration
Declare an interface Producer whose only method returns a T, marked so a Producer of String is usable where a Producer of Any is expected.
Show answer Hide answer
interface Producer<out T> {
fun produce(): T
}
val p: Producer<Any> = object : Producer<String> {
override fun produce() = "hi"
}out (covariance) is allowed because T only ever comes out.
projections and reified
7. Don’t care about the argument
Write a function size that accepts a List of any unknown element type and returns its size.
Show answer Hide answer
fun size(items: List<*>) = items.size* is the star projection — Kotlin’s equivalent of Java’s bare ?.
8. Keep the type at runtime
Write an extension asOrNull that safely casts any receiver to the requested type T, returning null on a mismatch — using reified.
Show answer Hide answer
inline fun <reified T> Any.asOrNull(): T? = this as? T
val name = value.asOrNull<String>()reified (only on inline functions) keeps T available at runtime, defeating erasure.
9. First element of a type
Write firstOfType — an inline extension on Iterable<*> with a reified T — returning the first element that is a T, or null.
Show answer Hide answer
inline fun <reified T> Iterable<*>.firstOfType(): T? =
firstOrNull { it is T } as T?reified keeps T at runtime so it is T compiles — possible only because the function is inline.
Back to the lesson, Kotlin Generics, or on to the next one: exceptions.