You can inline properties in Kotlin? Why is this useful?

Let’s take a trivial Android example:

val View.isVisible  
  get() = visibility == View.VISIBLE

That’s a simple yet convenient helper that hides the comparison operation behind a Boolean property, making your code look like this:

if (button.isVisible) {  
  Toast.makeText(context, "I'm a button!", Toast.LENGTH_SHORT).show()
}

isVisible is actually an extension property, since it’s declared on an existing View class. Extension properties, similar to extension functions, are just syntactic sugar on top of good old Java static methods, with the first method parameter becoming the receiver in the extension function. If we decompile the previous snippet, using the “Show Kotlin Bytecode” feature (if you’re in Android Studio, open Tools -> Kotlin -> Show Kotlin Bytecode), we’ll see the following Java code:

if(ViewKt.isVisible((View)button)) {  
  Toast.makeText(CheckNowView.this.getContext(), (CharSequence)"I'm a button!", 0).show();
}

As expected, the code invokes static isVisible() method in ViewKt class (the file with the extension property is called view.kt), passing button as the first argument. Let’s now mark our property’s getter with inline and see what it changes:

val View.isVisible  
  inline get() = visibility == View.VISIBLE

Decompiled:

View $receiver$iv = (View)button;  
if($receiver$iv.getVisibility() == 0) {  
  Toast.makeText(CheckNowView.this.getContext(), (CharSequence)"I'm a button!", 0).show();
}

As you see, the comparison operation has been inlined on the call site, removing the static method invocation. This code looks almost as if you’d write it in Java, but the Kotlin counterpart looks a lot neater.

Declaring Inline Properties

Here are a few notes on the usage of inline properties: with vars, you can mark both the getter and the setter inline:

var View.someProperty: String  
  inline get() = "Random value"
  inline set(value) {
    println("Value was set")
  }

If this is the case, you can actually mark the property itself inline, that will propagate the setting to both accessors:

inline var View.someProperty: String  
  /* inline */ get() = "Random value"
  /* inline */ set(value) {
    println("Value was set")
  }

inline is forbidden on properties that have a backing field:

// won't compile!
var upperCaseString: String = ""  
  inline get() = field.toUpperCase()

The rule also applies to the accessor that doesn’t reference the backing field:

// won't compile!
var upperCaseString: String = ""  
  get() = field.toUpperCase()
  inline set(value) {
    println("Field set!")
  }

And last but not least, keep in mind that inlining will often increase the overall bytecode size, so avoid inlining accessors that contain big chunks of code:

// not the best idea!
var aProperty: String  
  get() = ""
  inline set(value) {
    // a lot of code here!
  }

Conclusion

When used properly, inlining the properties can help the compiler produce more optimal byte code, removing unnecessary indirections. The trivial example we looked at might give you an idea about which accessors are worth inlining. In any case, rely on the decompiler to see exactly what the Java counterpart of your code looks like.

This tutorial was reposted from Egor’s Blog with modifications