skip to content

How can a class implement a generic interface, and what choices do you have for the interface's type parameter when you do?

level: middleimportance: should knowfreq 45%

answer

  1. Supply a type arg per interface parameter after the colon
  2. Fix it: class V : Comparable<V>
  3. Forward it: class Box<T> : Container<T>
  4. Partial fix: class IntMap<V> : Map<Int, V>
  5. override is mandatory; signature uses the substituted type

basics

~10 s

When a class implements a generic interface, it either fixes the type argument to a concrete type (class IntBox : Container<Int>) or stays generic and forwards its own parameter (class Box<T> : Container<T>).

solid answer

~40 s

A class implements a generic interface by supplying a **type argument** for each of the interface's type parameters in the supertype list. You have two choices. (1) **Fix** the argument to a concrete type: `class StringList : Comparable<StringList>` or `class Counter : Iterable<Int>` — the class is non-generic and the inherited members use the concrete type. (2) **Forward** the class's own parameter: `class Box<T> : Container<T>` — the class stays generic and passes its `T` straight through. You can also partially fix in multi-parameter interfaces: `class IntMap<V> : Map<Int, V>`. The overriding members must match the substituted signature exactly: implementing `Comparable<Foo>` means `override fun compareTo(other: Foo): Int`. The `override` keyword is mandatory in Kotlin.

code

kotlin · 16 lines
kotlin
interface Container<T> { fun get(): T }

// forward the parameter
class Box<T>(private val v: T) : Container<T> {
    override fun get(): T = v
}

// fix the parameter
class Version(val n: Int) : Comparable<Version> {
    override fun compareTo(other: Version): Int = n.compareTo(other.n)
}

fun main() {
    println(Box("hi").get())
    println(Version(2).compareTo(Version(1)))
}

go deeper

for a junior

Can implement a generic interface by fixing the type argument to a concrete type with a correct override.

for a middle

Knows both fixing and forwarding the class's own parameter, and that override signatures use the substituted type.

for a senior

Handles partial fixing in multi-parameter interfaces and explains how interface variance carries to implementers.

for a principal

Weighs whether to expose a concrete or still-generic implementation as the public type for API stability.

## Implementing a generic interface A generic interface declares one or more type parameters. When a class implements it, the class must **provide a type argument** for each parameter in the **supertype list** (after the colon). ## Option 1 — fix the type argument The implementing class is **not** generic; it nails the parameter to a concrete type: ```kotlin class Version(val n: Int) : Comparable<Version> { override fun compareTo(other: Version): Int = n.compareTo(other.n) } ``` Here `Comparable<in T>`'s `T` is fixed to `Version`, so `compareTo` takes a `Version`. The `override` keyword is **required**. ## Option 2 — forward the class's own parameter The implementing class **stays generic** and passes its own type parameter through: ```kotlin interface Container<T> { fun get(): T } class Box<T>(private val value: T) : Container<T> { override fun get(): T = value } ``` Now `Box<String>` is a `Container<String>`. The class's `T` and the interface's `T` are linked one-to-one. ## Option 3 — partial fixing (multi-parameter interfaces) With multiple parameters you can fix some and forward others: ```kotlin class IntKeyedMap<V> : Map<Int, V> { /* ... */ } ``` Here `K` is fixed to `Int`, `V` is forwarded. ## Signature matching Whatever you substitute, the **overriding member signatures must match the substituted interface signatures**. Implementing `Comparable<Version>` requires `override fun compareTo(other: Version): Int` — not `Any`, not `T`. Mismatches are compile errors. ## Variance carries over If the interface is declared `interface Source<out T>`, the relationship still holds for implementers: `Box<Cat> : Source<Cat>` is usable as `Source<Animal>`. You don't re-declare the variance on the implementing class; it comes from the interface. ## Extending generic classes The same rules apply to `open` generic classes via inheritance — `class StringBox : Box<String>()` fixes `T`, `class SubBox<T> : Box<T>()` forwards it. ## Summary - Provide a type argument per interface parameter in the supertype list. - Fix it (non-generic class), forward your own parameter, or fix some + forward others. - Override members with the substituted signature; `override` is mandatory.

  • Can a class fix some type parameters and forward others when implementing a two-parameter interface?
    Yes. e.g. `class IntMap<V> : Map<Int, V>` fixes K to Int and forwards V as its own parameter.
  • Does the overriding method use T or the substituted type in its signature?
    It must use the substituted type. Implementing Comparable<Version> requires `override fun compareTo(other: Version): Int`.

saying these in an interview costs you the question

  • Forgetting the `override` keyword (mandatory in Kotlin)
  • Writing the overriding method with `Any` or a raw type instead of the substituted type
  • Thinking a class must always stay generic to implement a generic interface
  • Re-declaring variance on the implementing class instead of inheriting it from the interface

context