Generic APIs should eliminate casts for callers
Name type parameters by their role when several are involved, and constrain them only as much as needed. A generic return type must be supported by an actual relationship to an input or stored value.
interface Repository<ID, Entity> {
fun find(id: ID): Entity?
}
data class Lesson(val id: String, val title: String)
class Lessons(private val entries: Map<String, Lesson>) : Repository<String, Lesson> {
override fun find(id: String): Lesson? = entries[id]
}
The repository fixes the relationship between its identifier and entity types. A function like fun <T> load(): T backed by an arbitrary cast promises more than it can guarantee unless a type-aware mechanism validates the result.
Do not force generics onto a domain-specific interface when concrete types communicate the contract better. Reuse should follow real common behavior.
Exercise
Add a repository using integer IDs and a different entity. Write callers that compile without casts and test a missing key.
Check: missing data should be represented explicitly; a generic type parameter is not permission to invent a value of that type.