内联函数无法访问非公共API:@PublishedApi vs @Suppress vs @JvmSynthetic

50
在Kotlin中,当我有一个非公共成员和一个调用它的inline fun时,会出现编译错误,错误信息如下:

Error:(22, 25) Kotlin: Public-API inline function cannot access non-public-API private fun f(): Unit defined in com.example

我发现几种方法可以在公共的inline fun中调用我的函数,但哪种方法是最好的呢?假设我有一个private fun f() { },那么我找到的选项如下:
  • fun f() { }

    只需将其设置为公共函数。这是基本解决方案,但如果其他解决方案证明有重大缺点,则此方法可能成为最佳选择。

  • @PublishedApi internal fun f() { }

    Kotlin 1.1-M04中引入的注释可以应用于内部成员,使其有效地变为公共成员。我注意到的含义是,任何库用户仍然可以从Java代码中调用它,这是我不喜欢的。

  • @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") inline fun g() { f() }

    stdlib源代码中发现,该注释似乎可以抑制调用函数时出现的错误。但它有什么限制吗?它只能用于inline函数吗?在某些情况下,程序会失败吗?我尝试使用此技巧从内联函数调用非内联函数,并且它可以工作,但看起来很可疑。

  • @JvmSynthetic @PublishedApi internal fun f() { }

    将第二种解决方案与字节码中的synthetic标志相结合。我不确定这是否是@JvmSynthetic的正确用法, 但这似乎将函数从Java代码中隐藏起来,从而解决了@PublishedApi internal的问题。

那么,这些解决方案中哪一个是从公共内联函数调用非公共函数的最佳方法?每种解决方案的缺点是我没有看到的吗?


2
"@PublishedApi internal" 应该声明在您的私有成员上,而不是内联函数上。 - Harry Timothy
1个回答

53

@PublishedApi internal 是公开内部API供公共内联函数使用的一种方法。

@PublishedApi internal 成员实际上变成了公共的,并且它的名称不会被重命名(如果您发现相反,请报告错误)。

@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")是在缺乏 @PublishedApi 的情况下,基于抑制错误的一种权宜之计,因此并不推荐使用。随着@PublishedApi的引入,这种抑制将被从标准库中清除。

@JvmSynthetic 结合使用 @PublishedApi 是一种有趣的方法,但在调试时可能会导致一些问题,尽管我不确定。


7
谢谢回复。没错,现在我明白 @PublishedApi internal 会保留名称,我之前误解了。顺便问一下,IDE 是否应该隐藏 @PublishedApi internal 成员以防止自动补全或以某种方式阻止使用它们?另外,出于好奇,使用 @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") 有什么问题吗? - hotkey
@hotkey 请问是否有关于在使用@PublishedApi@JvmSynthetic组合时会出现任何问题的后续信息? - Steven Jeuris
2
@StevenJeuris,我没有遇到过问题,尽管我不能说我经常使用这种组合。好吧,我的一些实验旨在检测问题,但都没有显示出任何问题。 - hotkey
1
@hotkey @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") 这不仅仅是为了消除警告,对吧?我的意思是它所做的就是 @PublishedApi 的本意——"使成员有效地公开",我说得对吗? - Farid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接