I have a generic function which needs to instantiate an object of its generic argument and pass it to an instance of some interface.
As far as I know, the only way to instantiate that generic object is making the function inline and reifying that type parameter. But I do not want to expose the implementation of that interface.
The problem is that inlined functions can not use internal classes.
What I basically want to is this:
/* The interface I want to expose */
interface Params<T> {
val doc: T
}
/* The implementation I do not want to expose */
internal class ParamsImpl<T> (override val doc: T) : Params<T>
/* The function */
inline fun <reified T> doSomething(init: Params<T>.() -> Unit) {
val doc= T::class.java.newInstance()
val params = ParamsImpl(doc) // Can't compile since ParamsImpl is internal
params.init()
}
You can fix this by adding an intermediary method to create your ParamsImpl
instance:
fun <T> createImpl(doc: T): Params<T> = ParamsImpl(doc)
inline fun <reified T> doSomething(init: Params<T>.() -> Unit) {
val doc = T::class.java.newInstance()
val params = createImpl(doc)
params.init()
}
This method doesn't have to be inlined (since you're just passing a generic instance to it that you've already created in the doSomething
method), therefore it can use ParamsImpl
in its private implementation.
It's also important that it's return type is Params<T>
so that it only exposes that type to the users of doSomething
.
Edit:
To hide the creation method, you can use the @PublishedApi
annotation in combination with internal
visibility:
@PublishedApi internal fun <T> createImpl(doc: T): Params<T> = ParamsImpl(doc)
This will enable its use in inline
functions, but hide it from other users.
This means that in other modules, it won't be visible in Kotlin code, and it will give you an error when you try to call it from Java (this, of course, can be suppressed, but this is the best guarantee that Kotlin can give you for its internal
visiblity when doing interop).
You can also use the constructor and mark the ParamsImpl
class itself with @PublishedApi
, if you like that more.