【问题标题】:Reified type parameters and internals [duplicate]具体类型参数和内部[重复]
【发布时间】:2017-12-31 00:22:10
【问题描述】:

我有一个通用函数,它需要实例化其通用参数的对象并将其传递给某个接口的实例。
据我所知,实例化该通用对象的唯一方法是使函数内联并具体化该类型参数。但我不想公开该接口的实现。
问题是内联函数不能使用内部类。

我基本上想要的是这样的:

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

【问题讨论】:

    标签: generics kotlin inline


    【解决方案1】:

    您可以通过添加中间方法来创建您的 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&lt;T&gt; 也很重要,这样它只会将该类型公开给doSomething 的用户。


    编辑:

    要隐藏创建方法,可以使用@PublishedApi注解结合internal可见性:

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

    这将允许在inline 函数中使用它,但对其他用户隐藏它。

    这意味着在其他模块中,它在 Kotlin 代码中是不可见的,并且当您尝试从 Java 调用它时会报错(当然可以抑制,但这是最好的保证 Kotlin 在进行互操作时可以为您提供 internal 可见性)。

    如果您更喜欢这样,您也可以使用构造函数并将ParamsImpl 类本身标记为@PublishedApi

    【讨论】:

    • 该解决方案的问题是我必须公开我不想公开的createImpl。我只希望能够在内部创建Params
    • 哇,真是太棒了
    【解决方案2】:

    这是因为内联函数将被内联到调用点函数中,并且ParamsImpl 类的可见性是internal

    您可以通过使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暴露给别人,你必须使用reflection,例如:

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

    【讨论】:

    • doSomething 需要公开。拥有一个创建 Params 的公共函数不是我想要的,因为我只想能够在内部创建它们
    • @danielspaniol 先生,现在怎么样?
    • 看起来真的很难看。我希望有比使用反射更好的解决方案。我不明白为什么不能在inline 函数中只使用internal
    • @danielspaniol 嗨,没有办法这样做,因为ParamImpl 将内联到调用站点函数中,但是它的可见性是内部的,所以编译器会报告这样的错误。如果不想暴露内部ParamsImpl,唯一的办法就是使用reflection
    • 我认为既然 Kotlins internal 映射到 Javas public 它应该不会对编译器造成问题
    猜你喜欢
    • 2014-12-31
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多