Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline function cannot access non-public-API: @PublishedApi vs @Suppress vs @JvmSynthetic

In Kotlin, when I have a non-public member and an inline fun that calls it, there's a compilation error saying:

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

I found several ways to call my function inside a public inline fun, but which is the best way to do it?

Suppose I have a private fun f() { }. Then the options I found are:

  • fun f() { }

    Just make it public. This is the baseline solution, but if the others turn out to have major disadvantages, this can end up the best one.

  • @PublishedApi internal fun f() { }

    Introduced in Kotlin 1.1-M04, the annotation can be applied to an internal member, making it effectively public. The implication I noticed is that any library user will still be able to call it from Java code, that's what I don't like about it.

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

    Found in the stdlib sources, this annotation seems to suppress the error when applied to the calling function. But what are its limitations? Can it only be used for inline functions? Will the resulting program fail under some circumstances? I tried to call a non-inline function from an inline one with this trick, and it worked, but it looks suspicious.

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

    Combine the second solution with the synthetic flag in the bytecode. I'm not sure if this is a correct usage of @JvmSynthetic, but this appears to hide the function from Java code, which solves the problem of the @PublishedApi internal.

So, which of these solutions is the best way call a non-public function from a public inline one? What are the downsides of each solution that I don't see?

like image 215
hotkey Avatar asked Jan 27 '17 11:01

hotkey


1 Answers

@PublishedApi internal is the intended way of exposing non-public API for using in public inline functions.

That @PublishedApi internal member becomes effectively public and its name doesn't get mangled (if you noticed the opposite, please file a bug).

@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") is a band-aid workaround in the lack of @PublisedApi based on suppressing an error and therefore isn't recommended. With the introduction of @PublishedApi this suppression is going to be cleaned from stdlib.

@JvmSynthetic combined with @PublishedApi is an interesting approach, however it can cause some problems while debugging, though I'm not sure.

like image 108
Ilya Avatar answered Sep 21 '22 13:09

Ilya