Bird
Raised Fist0
Kotlinprogramming~30 mins

Out variance (covariance) in Kotlin - Mini Project: Build & Apply

Choose your learning style10 modes available
Out Variance (Covariance) in Kotlin
📖 Scenario: Imagine you are creating a simple program to manage a list of animals and their sounds. You want to understand how Kotlin's out variance (covariance) works when dealing with lists of different animal types.
🎯 Goal: You will build a Kotlin program that demonstrates out variance by creating a producer interface and using it with a list of animals and their subclasses.
📋 What You'll Learn
Create a base class Animal with a function makeSound() that prints a generic sound.
Create two subclasses Dog and Cat that override makeSound() with specific sounds.
Create an interface AnimalProducer with an out variance type parameter and a function produce() that returns an animal.
Create a function that accepts an AnimalProducer<Animal> and calls makeSound() on the produced animal.
Demonstrate covariance by passing an AnimalProducer<Dog> to the function that expects AnimalProducer<Animal>.
💡 Why This Matters
🌍 Real World
Understanding covariance helps when working with collections or producers that return objects of different but related types, such as UI components, data models, or event handlers.
💼 Career
Many Kotlin and Java jobs require knowledge of generics and variance to write safe and reusable code, especially in Android development and backend services.
Progress0 / 4 steps
1
Create Animal classes
Create a base class called Animal with a function makeSound() that prints "Some generic animal sound". Then create two subclasses called Dog and Cat that override makeSound() to print "Bark" and "Meow" respectively.
Kotlin
Hint
Remember to use open keyword for the base class and override for subclass methods.
2
Create AnimalProducer interface with out variance
Create an interface called AnimalProducer with an out variance type parameter T. Add a function produce() that returns T.
Kotlin
Hint
Use out before the type parameter T to make it covariant.
3
Implement DogProducer and function to use AnimalProducer
Create a class DogProducer that implements AnimalProducer<Dog>. Implement produce() to return a new Dog. Then create a function called makeAnimalSound that accepts an AnimalProducer<Animal> and calls makeSound() on the produced animal.
Kotlin
Hint
Remember to override produce() in DogProducer and call makeSound() on the produced animal in makeAnimalSound.
4
Call makeAnimalSound with DogProducer
Create an instance of DogProducer called dogProducer. Then call makeAnimalSound(dogProducer) to demonstrate covariance. This should print "Bark".
Kotlin
Hint
Create the DogProducer instance and pass it to makeAnimalSound to see the output.

Practice

(1/5)
1. What does the out keyword mean in Kotlin generics?
easy
A. The generic type can be used for both input and output.
B. The generic type is used only for input (consuming values).
C. The generic type is used only for output (producing values).
D. The generic type is mutable and can be changed.

Solution

  1. Step 1: Understand the meaning of out in Kotlin generics

    The out keyword means the generic type is only used to produce values, not consume them.
  2. Step 2: Compare options with this meaning

    The generic type is used only for output (producing values). correctly states that the generic type is used only for output (producing values). Other options describe input or mutability, which are incorrect.
  3. Final Answer:

    The generic type is used only for output (producing values). -> Option C
  4. Quick Check:

    Out variance = output only [OK]
Hint: Remember: out means output only, no input allowed [OK]
Common Mistakes:
  • Confusing out with in variance (input)
  • Thinking out allows mutation
  • Assuming out means both input and output
2. Which of the following is the correct syntax to declare a covariant generic interface in Kotlin?
easy
A. interface Producer { fun produce(): T }
B. interface Producer { fun produce(): T }
C. interface Producer<T> { fun produce(input: T) }
D. interface Producer<T> { fun produce(): T }

Solution

  1. Step 1: Identify covariance syntax in Kotlin

    Covariance is declared using out before the generic type parameter, like out T.
  2. Step 2: Match syntax with options

    interface Producer { fun produce(): T } correctly uses out T and a function that returns T. Other options either use in or lack the out keyword.
  3. Final Answer:

    interface Producer<out T> { fun produce(): T } -> Option A
  4. Quick Check:

    Covariant syntax = out T [OK]
Hint: Covariant generics use 'out' before the type parameter [OK]
Common Mistakes:
  • Using 'in' instead of 'out' for covariance
  • Omitting 'out' keyword
  • Confusing input and output functions
3. What will be the output of the following Kotlin code?
interface Source {
    fun next(): T
}

class StringSource : Source<String> {
    override fun next() = "Hello"
}

fun printSource(source: Source<Any>) {
    println(source.next())
}

fun main() {
    val stringSource = StringSource()
    printSource(stringSource)
}
medium
A. Hello
B. Compilation error due to type mismatch
C. Runtime error
D. Prints null

Solution

  1. Step 1: Understand covariance in the interface

    The interface Source is declared with out T, so Source<String> can be used where Source<Any> is expected.
  2. Step 2: Trace the function call and output

    stringSource returns "Hello" from next(). Passing it to printSource works due to covariance, so it prints "Hello".
  3. Final Answer:

    Hello -> Option A
  4. Quick Check:

    Covariance allows subtype substitution = Hello [OK]
Hint: Covariance lets Source<String> be used as Source<Any> safely [OK]
Common Mistakes:
  • Expecting a compilation error due to type mismatch
  • Confusing covariance with invariance
  • Thinking runtime error occurs
4. Identify the error in this Kotlin code using out variance:
interface Producer {
    fun produce(input: T): T
}
medium
A. No error, code is correct
B. Missing return type in function produce
C. Interface must be declared with in instead of out
D. Cannot use T as a function parameter when declared with out

Solution

  1. Step 1: Check usage of generic type T with out variance

    When T is declared with out, it can only be used as a return type, not as a function parameter.
  2. Step 2: Analyze the function signature

    The function produce(input: T): T uses T as a parameter, which is not allowed with out T.
  3. Final Answer:

    Cannot use T as a function parameter when declared with out -> Option D
  4. Quick Check:

    Out variance forbids T as input parameter [OK]
Hint: Out means T only as output, not input parameters [OK]
Common Mistakes:
  • Ignoring variance rules for function parameters
  • Thinking out allows input parameters
  • Confusing in and out variance
5. Given the following Kotlin classes:
open class Animal
class Dog : Animal()

interface Producer {
    fun produce(): T
}

fun feedAnimals(producer: Producer<Animal>) {
    val animal = producer.produce()
    println("Feeding an animal")
}

fun main() {
    val dogProducer: Producer<Dog> = object : Producer<Dog> {
        override fun produce() = Dog()
    }
    feedAnimals(dogProducer)
}

Why does this code compile and run without errors?
hard
A. Because Kotlin ignores variance annotations at runtime
B. Because Producer is covariant with out, allowing Producer<Dog> where Producer<Animal> is expected
C. Because produce() returns a generic type without restrictions
D. Because Dog is a superclass of Animal, so substitution is allowed

Solution

  1. Step 1: Understand covariance with out keyword

    The interface Producer is declared with out T, making it covariant. This means Producer<Dog> can be used where Producer<Animal> is expected.
  2. Step 2: Analyze class hierarchy and function call

    Dog is a subtype of Animal. The function feedAnimals expects Producer<Animal>, but dogProducer is Producer<Dog>. Covariance allows this substitution safely.
  3. Final Answer:

    Because Producer is covariant with out, allowing Producer<Dog> where Producer<Animal> is expected -> Option B
  4. Quick Check:

    Covariance allows subtype substitution [OK]
Hint: Covariance lets Producer<Dog> be used as Producer<Animal> [OK]
Common Mistakes:
  • Thinking Dog is superclass of Animal
  • Ignoring variance and expecting error
  • Assuming produce() return type is unrestricted
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard