Как я могу хранить данные овеществленного типа в полях экземпляра в Kotlin?


В настоящее время я пишу DSL для библиотеки, и я хотел бы предоставить метаданные типа, используя такие параметры овеществленного типа:

    val config = Config.create()
            .consumerFor<MyType>{
              // consume
            }

Моя проблема заключается в том, что я могу использовать только ключевое слово reified в функциях inline, а в функции inline я не могу использовать такие поля экземпляра:

    inline fun <reified T> consumerFor(consumer: (T) -> Unit) {
        consumers.put(T::class.java, consumer)
        return this
    }

Потому что я получаю ошибку:

Встроенная функция Public-API не может получить доступ к частным конечным потребителям непубличного API...

Похоже, что пока я не могу использовать овеществленный тип параметры, где они будут наиболее полезны. Есть ли обходной путь для этого?

2 6

2 ответа:

Public inline функции не могут использовать объявления private напрямую, потому что, когда они встроены в сайты вызовов вне класса, использование будет иметь неверный уровень доступа (в JVM член класса private не может быть доступен извне).

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

Существует несколько способов доступа к члену internal из public inline fun, см. Этот вопрос: (Ссылка)

В вашем конкретном случае я предпочел бы сделать это с помощью @PublishedApi:

private val consumers = mutableMapOf<Class<*>, Any>()

@PublishedApi
internal fun <T> putConsumer(clazz: Class<out T>, consumer: (T) -> Unit) {
    consumers.put(clazz, consumer)
}

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit): C {
    putConsumer(T::class.java, consumer)
    return this
}

Или, если вы не против разоблачить consumers с @PublishedApi, то вы можете сделать это следующим образом:

@PublishedApi
internal val consumers = mutableMapOf<Class<*>, Any>()

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit): C {
    consumers.put(T::class.java, consumer)
    return this
}

Если все, что вам нужно, чтобы параметр reified type отражал его класс java, то вы можете управлять нестрочной перегрузкой с дополнительным параметром Class<T>.

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit) =
    consumerFor(T::class.java, consumer)

fun <T> consumerFor(key: Class<T>, consumer: (T) -> Unit) = apply {
    consumers.put(key, consumer)
}

Таким образом, вы не выставляете consumers свойство эффективно опубликованным. Суть бонуса заключается в том, что можно назвать не-встроенный перегрузки и от Java.