[英]How can I store reified type data in instance fields in Kotlin?
I'm currently writing a DSL for a library and I'd like to supply type metadata using reified type parameters like this: 我正在为一个库编写一个DSL,我想使用这样的具体类型参数提供类型元数据:
val config = Config.create()
.consumerFor<MyType>{
// consume
}
My problem is that i can only use the reified
keyword in inline
functions and in an inline
function I can't use instance fields like this: 我的问题是,我只能用reified
关键字inline
函数和一个inline
函数,我不能用这样的实例字段:
inline fun <reified T> consumerFor(consumer: (T) -> Unit) {
consumers.put(T::class.java, consumer)
return this
}
because I get an error: 因为我收到一个错误:
Public-API inline function cannot access non-public-API 'private final val consumers... Public-API内联函数无法访问非公共API的私有最终val消费者...
It seems so far that I can't use reified type parameters where they would be most useful. 到目前为止,我似乎无法使用最有用的reified类型参数。 Is there a workaround for this? 这有解决方法吗?
Public inline
functions cannot use private
declarations directly, because, when inlined at the call sites outside the class, the usages will have incorrect access level (on JVM, a private
member of a class cannot be accessed from outside). 公共inline
函数不能直接使用private
声明,因为当在类外部的调用站点内联时,用法将具有不正确的访问级别(在JVM上,无法从外部访问类的private
成员)。
What you can do is use the internal
visibility in Kotlin: on JVM, the members with this visibility modifier will be compiled into public members with their names mangled (therefore still visible but not easy-to-call from Java), and the Kotlin compiler will at least control the usages from the Kotlin code. 您可以做的是使用Kotlin中的internal
可见性 :在JVM上,具有此可见性修饰符的成员将被编译为公共成员,其名称被破坏(因此仍然可见但不容易从Java调用)和Kotlin编译器至少会控制Kotlin代码的用法。
There are a few ways to access an internal
member from within a public inline fun
, see this question: (link) 有一些方法可以从public inline fun
访问internal
成员,请参阅此问题:( 链接)
In your particular case, I would prefer doing it with @PublishedApi
: 在您的特定情况下,我更喜欢使用@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
}
Or, if you don't mind exposing consumers
with @PublishedApi
, then you can do it as follows: 或者,如果您不介意使用@PublishedApi
公开consumers
,那么您可以按如下方式进行:
@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
}
If all that you need the reified type parameter for is to reflect its java class, then you can manage with a non-inline overload with additional Class<T>
parameter. 如果您需要reified类型参数的所有内容都是为了反映其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)
}
This way you haven't expose consumers
property effectively published. 这样您就没有公开consumers
有效发布的财产。 The bonus point is that you can call that non-inline overload also from Java. 奖励点是您也可以从Java调用非内联重载。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.