Blog Infos
Author
Published
Topics
, , , ,
Published
How does it lead to binary compatibility issues
Introduction:

Most developers will know the basic definition of an Inline Function. Inline functions tell the compiler to insert all the lines of code present in them at the call site (from where it is called).

For example

If you create an inline function

inline fun printSomething() {
println("Hi")
}

Suppose if you call this from your activity’s onCreate(),

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        printSomething()
}

If you decompile the above code by going to Tools -> Kotlin -> Show Kotlin Bytecode and then select Decompile, you will see the converted code in Java (text highlighted in blue) as shown in the screenshot below.

But our compiler might warn us about the above inline function like below.

So let’s discuss the correct use case on when to make a function inline

Use case:

We can make a function inline when we have parameters of functional types which is nothing but a lambda expression.

So let’s modify the above function with a lambda parameter

inline fun printSomething(lambda: () -> Unit) {
        println("Hi")
        lambda()
}

Now the compiler doesn’t give any warning. Nice!

And the above function should be called like

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        printSomething { }  // Just kept it empty, any code can be used as well
}

So the code of lambda expression inside the above function will be inserted into the call site which reduces the function call overhead.

But we shouldn’t create inline functions unnecessarily as it can increase the size of the generated bytecode.

Access Restrictions:

Private modifier access restrictions of an inline function

Within an inline function, we can’t access a private property.

For example, if there is a private variable

private lateinit var name: String

And we try to access it inside an inline function like below,

inline fun printSomething(lambda: () -> Unit) {
    println("Hi")
    lambda()
    name = ""
}

We will get the following compilation error

So we have two options now,

  • Make the inline function also private
private inline fun printSomething(lambda: () -> Unit) {
    println("Hi")
    lambda()
    name = ""
}
  • Use internal modifier on name variable instead of private and annotate it with @PublishedApi
@PublishedApi
internal lateinit var name: String
inline fun printSomething(lambda: () -> Unit) {
    println("Hi")
    lambda()
    name = ""
}

 

How does the above conjunction (internal and @PublishedApi) help?

To allow an inline function to access non-public properties or API, the @PublishedApi annotation can be used in conjunction with internal visibility. This effectively makes the internal declaration public for inlining, while still restricting its use outside the module.

Now, let’s explore why inline functions were designed not to access private properties

Why can’t inline functions access private properties or variables?

Inline functions in Kotlin are designed for performance optimization by replacing the function call with the actual code of the function at compile time. Due to this mechanism, inline functions cannot access private members of a class because the inlined code becomes part of the calling scope, which may not have access to those private members. If an inline function could access private members, it would break encapsulation and potentially lead to unintended side effects or security vulnerabilities.

What is binary compatibility?

Binary compatibility in Kotlin refers to the ability of different versions of a library or module to work together without requiring recompilation. Issues arise when changes in a newer version of a library break compatibility with code compiled against an older version. These issues can manifest as runtime errors, linkage errors, or unexpected behavior.

How do binary compatibility issues occur while using inline functions?
  • When dealing with public inline functions and private declarations, binary compatibility issues can arise
  • If the inline function uses private or internal declarations, and these declarations change, modules that have already been compiled against the previous version may encounter binary incompatibility issues if they are not recompiled.
Solution?
  • To avoid these issues, public inline functions are restricted from using non-public declarations (private and internal). This restriction ensures that changes to the internal implementation of a module do not break binary compatibility with other modules.
  • If an internal declaration needs to be used in a public API inline function, it can be annotated with @PublishedApi

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Meta-programming with K2 compiler plugins

Let’s see what’s possible with plugins using the new K2 compiler, FIR. This live demo session will go through possible use cases that reduce boilerplate code and make your code safer.
Watch Video

Meta-programming with K2 compiler plugins

Tadeas Kriz
Senior Kotlin Developer
Touchlab

Meta-programming with K2 compiler plugins

Tadeas Kriz
Senior Kotlin Develo ...
Touchlab

Meta-programming with K2 compiler plugins

Tadeas Kriz
Senior Kotlin Developer
Touchlab

Jobs

Conclusion:

As a summary, use inline functions when

  • We want to reduce the function call overhead
  • The function takes a lambda expression as a parameter
  • Only the proper use cases mentioned (access restrictions) above are covered

Thanks for reading this article. If you like this post, Please give a clap (👏).

Also, if you like to support me through
https://buymeacoffee.com/dilipchandar, please do.

Let’s connect on LinkedIn https://www.linkedin.com/in/dilip-chandar-97570158?

This article was previously published on proandroiddev.com.

Menu