I have a generic function for querying an API that looks something like:
inline fun <reified T : Any> queryApiX(
queryName: String,
accountToken: String,
): List<T> {
val jsonNode = makeRequest(queryName, accountToken)
return deserialize<T>(jsonNode)
}
What I’d like is to be able to define the bound T : XQueryableEntity, such that all implementations of XQueryableEntity must have a static/companion object member “queryName”, so none of my call sites have to know about and repeat the query name and it can just be defined on the type:
class XUser : XQueryableEntity {
companion object {
val queryName = "getUsers" // compile-time checked that this exists
}
}
inline fun <reified T : XQueryableEntity> queryApiX(
accountToken: String,
): List<T> {
// accessible via bounded type parameter
val jsonNode = makeRequest(T::queryName, accountToken)
return deserialize<T>(jsonNode)
}
val users: List<XUser> = queryApiX(accountToken)
But there doesn’t seem to be any way to inherit any kind of static property that can be accessed in a type-safe way on a type parameter without having an instance of it.
This doesn’t help, the point is I need the queryName before I have any given XUser. I should clarify I’ve pretty much gathered you can’t express this kind of requirement, this is essentially a feature request.
One option would be to have your users pass in the implementation explicitly, but it can still hold the type, so that it does double duty as a type inference hint for T:
interface XQueryableEntityCompanion<T: Any> {
val queryName: String
}
inline fun <reified T : Any> queryApiX(
companion: XQueryableEntityCompanion<T>,
accountToken: String,
) = ...
// You can even theoretically have this unsafe method if you want:
inline fun <reified T : Any> queryApiX(
accountToken: String,
) = queryApiX<T>(T::class.companionObjectInstance, accountToken) // requires kotlin.reflect.full
//Usage
class XUser {
companion object: XQueryableEntityCompanion<XUser> {
val queryName = "getUsers"
}
}
queryApiX(XUser, "foo)