What are type erasure and variance in generics, and how do Swift, Kotlin and Java differ?
LanguagesSenior
In shortJava and Kotlin erase generic type arguments at runtime while Swift keeps them. Variance decides whether a container of a subtype counts as a container of its supertype; Kotlin declares it with out and in, Java uses wildcards, and Swift generics are invariant except for built-in collections.
Model answer
Type erasure: Java and Kotlin check generic types at compile time and drop the type arguments in the bytecode. At runtime ArrayList<String> and ArrayList<Integer> are the same class, so you can’t write x instanceof List<String>, new T(), T.class, or two overloads that differ only by type argument, and JSON libraries need a TypeToken/TypeReference to learn that a list holds Users. Kotlin’s inline fun <reified T> keeps T at the call site. Swift keeps generic types at runtime and can specialize generic code per type.
Variance: if Cat is an Animal, may a container of cats be used as a container of animals?
- Reading only (producer): yes, that’s covariance. Kotlin:
List<out E>. Java:List<? extends Animal>. - Writing only (consumer): the other way round, contravariance. Kotlin:
Comparator<in T>. Java:List<? super Cat>. - Both: neither; invariant.
MutableList<Cat>isn’t aMutableList<Animal>, or you could add aDogto a list of cats.
Java’s rule of thumb is PECS, producer extends, consumer super. Swift generics are invariant, with the standard collections special-cased ([Cat] converts to [Animal] safely because arrays are values).
Senior bonus: Swift’s some P is one hidden concrete type checked statically, while any P is an existential box with dynamic dispatch and possible heap allocation, so prefer generics and some in hot code.
What they're looking for
Concrete consequences of erasure (no T::class, TypeToken, no overloads by type argument), the read/write reasoning behind variance, PECS, Kotlin out/in and reified, and Swift's reified generics and some/any.
Common mistakes
- Saying all three languages erase generics.
- Not being able to explain why MutableList<Cat> isn't a MutableList<Animal>.
- Confusing Swift's some and any.
More languages questions
- How is memory managed in Swift compared to Kotlin or Java?Mid-level
- What is the difference between a value type and a reference type?Junior
- What is the difference between checked and unchecked exceptions, and how do Kotlin and Swift handle this?Mid-level
- When would you choose composition over inheritance?Mid-level