Параметры и внутренние параметры рефицированного типа

У меня есть общая функция, которая должна создать экземпляр объекта его общего аргумента и передать его экземпляру некоторого интерфейса.
Насколько я знаю, единственный способ создать экземпляр этого общего объекта – сделать функцию inline и подтвердить этот параметр типа. Но я не хочу раскрывать реализацию этого интерфейса.
Проблема в том, что встроенные функции не могут использовать внутренние классы.

В основном я хочу:

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

Вы можете исправить это, добавив посреднический метод для создания экземпляра 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() } 

Этот метод не должен быть встроен (поскольку вы просто передаете ему общий экземпляр, который вы уже создали в методе doSomething ), поэтому он может использовать ParamsImpl в своей частной реализации.

Также важно, чтобы это возвращаемый тип Params<T> поэтому он только предоставляет этот тип пользователям doSomething .


Редактировать:

Чтобы скрыть метод создания, вы можете использовать аннотацию @PublishedApi в сочетании с internal видимостью:

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

Это позволит использовать его в inline функциях, но скрыть их от других пользователей.

Это означает, что в других модулях он не будет отображаться в коде Kotlin, и он даст вам сообщение об ошибке при попытке вызвать его с Java (это, конечно же, можно подавить, но это лучшая гарантия того, что Kotlin может дать вам internal видимость при выполнении взаимодействия).

Вы также можете использовать конструктор и пометить ParamsImpl класс @PublishedApi с помощью @PublishedApi , если вам это нравится больше.

Это связано с тем, что встроенная функция будет включена в функцию сайта вызова, а видимость класса ParamsImpl является внутренней .

Вы можете легко решить эту проблему, doSomething для внутреннего .

Но вы также можете извлечь другой не-встроенный метод для решения проблемы, например:

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

Если вы не хотите показывать ParamsImpl другим, вы должны использовать отражение , например:

 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").getDeclaredConstructor(Any::class.java).run { isAccessible = true @Suppress("UNCHECKED_CAST") (newInstance(doc) as Params<T>).init() } }