KOTLIN / CHAPTER 11
Generic APIs and variance
AdvancedExpress reusable producer and consumer capabilities safely.
By the end of this chapter
- Use type parameters for relationships
- Distinguish producers from consumers
- Avoid unchecked casts as an API design
The idea
A type parameter connects the types of related inputs and outputs. A function that returns the first element as T preserves information that would be lost by returning Any. Constraints limit the operations available inside the generic implementation. They should reflect the actual algorithm: comparing elements for order requires more than simply accepting arbitrary values of T.
Variance describes safe substitution between parameterized types. A producer that only supplies T can declare out T, allowing a producer of strings to be viewed as a producer of Any. A consumer can use in T when it only accepts values. Mutable collections both read and write their element type, so treating a MutableList<String> as MutableList<Any> would allow insertion of an incompatible value.
interface Source<out T>{fun next():T}
class WordSource:Source<String>{override fun next()="ready"}
fun main(){val source:Source<Any> = WordSource();println(source.next())}ready
The producer exposes only returned values, so a string-producing source can safely satisfy a consumer expecting Any results.
Try it yourself
Write fun <T> firstOrNull(values: List<T>): T? and define the empty-input result.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING