Parámetros de tipo reificado e internos

Tengo una function genérica que necesita crear una instancia de un object de su argumento genérico y pasarlo a una instancia de alguna interfaz.
Hasta donde yo sé, la única forma de instanciar ese object genérico es haciendo que la function esté en línea y reificando ese parámetro de tipo. Pero no quiero exponer la implementación de esa interfaz.
El problema es que las funciones en línea no pueden usar classs internas.

Lo que básicamente quiero es esto:

/* 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() } 

Puede solucionar esto agregando un método intermedio para crear su instancia de ParamsImpl :

 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() } 

Este método no tiene que estar en línea (ya que solo le está pasando una instancia genérica que ya ha creado en el método doSomething ), por lo tanto, puede usar ParamsImpl en su implementación privada.

También es importante que su tipo de devolución sea Params<T> para que solo exponga ese tipo a los usuarios de doSomething .


Editar:

Para ocultar el método de creación, puede usar la anotación @PublishedApi en combinación con internal visibilidad internal :

 @PublishedApi internal fun <T> createImpl(doc: T): Params<T> = ParamsImpl(doc) 

Esto permitirá su uso en funciones en inline , pero lo esconderá de otros usuarios.

Esto significa que en otros modules, no estará visible en el código de Kotlin, y le dará un error cuando intente llamarlo desde Java (esto, por supuesto, puede ser suprimido, pero esta es la mejor garantía de que Kotlin puede darte por su visibilidad internal al hacer interoperabilidad).

También puedes usar el constructor y marcar la class @PublishedApi con @PublishedApi , si te gusta más.

Esto se debe a que una function en línea se includeá en la function del sitio de llamada y la visibilidad de la class ParamsImpl es interna .

Puede resolver fácilmente este problema haciendo que la visibilidad de doSomething sea interna .

Pero también puede extraer otro método no en línea para resolver el problema, por ejemplo:

 inline fun <reified T> doSomething(init: Params<T>.() -> Unit) { val doc= T::class.java.newInstance() newParams(doc).init() } fun <T> newParams(doc: T): Params<T> { return ParamsImpl<T>(doc) } 

Si no desea exponer ParamsImpl a otros, debe usar el reflection , por ejemplo:

 inline fun <reified T> doSomething(init: Params<T>.() -> Unit) { val doc = T::class.java.newInstance() // v--- the full qualifier class name Class.forName("pkg.ParamsImpl").getDeclanetworkingConstructor(Any::class.java).run { isAccessible = true @Suppress("UNCHECKED_CAST") (newInstance(doc) as Params<T>).init() } }