Inline fun mixed visibility

I’m using a lot of inline functions because I need reified types. It’d be nice if they didn’t have to be inline but that’s not my point.

The problem seems to be that if any of these inlined functions is public they must all be public. This causes an infection of publicness that inhibits encapsulation.

My supposition is that they need access as if they were called from the inlining call site even if they are only called within the module (file).

I suppose I could explicitly pass the class in instead of using a reified generic parameter. Any other workarounds come to mind?

1 Like

See @PublishedApi.

3 Likes

Nice! Thank you.

1 Like

Note that you may not want to use inline functions that much, as:
• they will impact performance (because multiple occurrences of the same pieces of code will be jitted separately);
• they will make your binaries grow insanely big;
• there is the publicity issue you came here for (which can be alleviated using @PublishedApi, but it means still making members internal, when they could possibly be private).

A better solution would be making inline functions as shorthands over passing the type explicitly,

inline fun <reified T> foo() =
    foo(tClass = T::class)

@PublishedApi
internal fun foo(tClass: KClass<*>) {
    // Implement your function here, as you will be able to access unpublished members.
}

That being said, inline functions can be useful to avoid unnecessary overhead for functions that do very simple things, such as only calling another function or doing basic arithmetic operations. Even in these cases, though, the benefit is usually negligible, as the JVM will inline those for you (it should only improve unoptimized execution, like for code that that is not considered a hot path by the JVM).
The use case inlining is actually most useful for is higher–order functions — that is, functions taking other functions as their arguments. In this case, try to logically split them in parts that reference functional parameters and parts that do not. If the latter are long enough, you may benefit from extrapolating those parts in non–inlined functions and call them from the inlined one. This is also useful to let those parts access private members:

class Foo {

    private var isInBlock = false;

    @PublishedApi
    internal fun beginBlock() {
        isInBlock = true;
    }

    @PublishedApi
    internal fun endBlock() {
        isInBlock = false;
    }

    inline fun runBlock(block: () -> Unit) {

        beginBlock();

        try {
            block();
        } finally {
            endBlock();
        }
    }
}
5 Likes

This is super. Thanks a bunch.

1 Like