Ad

How Does Kotlin Lambda Perform Compared To Anonymous Inner Class?

- 1 answer

Android Studio suggests to replace anonymous inner class with lambda.

titleTextView.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        Log.d("MY_TAG", "textView clicked in anonymous inner class")
    }
})

Decompiled Java code:

var10000 = this.titleTextView;
if (this.titleTextView == null) {
    Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}

var10000.setOnClickListener((OnClickListener)(new OnClickListener() {
    public void onClick(@Nullable View v) {
        Log.d("MY_TAG", "textView clicked in anonymous inner class");
    }
}));

Before lambda, to avoid creation of new object for each of views that were set OnClickListener, it was better to have Activity/Fragment implement View.OnClickListener interface or use Butterknife's @OnClick annotation.

How different the performance will be with lambda like below?

titleTextView.setOnClickListener { Log.d("MY_TAG", "textView clicked in lambda") }

Decompiled Java code:

TextView var10000 = this.titleTextView;
if (this.titleTextView == null) {
    Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}

var10000.setOnClickListener((OnClickListener)null.INSTANCE);

In case of lambda I don't see the Log.d("MY_TAG", "textView clicked in lambda") in decompiled code.

Ad

Answer

The performance of a lambda will be at least as good as creating an anonymous inner class.

  • If it doesn't capture any references, the compiler will make it a singleton inside whatever class it's used in as an optimization measure. This is what happens in your case, as the contents of your listener don't refer to anything outside of the lambda. (This singleton instance is what the null.INSTANCE is trying to refer to, the decompiler just has trouble with resolving the name of the class generated for the lambda.) So in this case, the cost of the lambda is just 1 object allocation.

  • If your lambda does capture something, e.g. like this:

    val random = Random().nextInt()
    titleTextView.setOnClickListener { 
        Log.d("MY_TAG", "textView clicked in lambda, random value was $random") 
    }
    

    ... then whenever you set the listener, a new instance will have to be allocated, because these instances have to store references to variables, which might be different each time they're created. In this case, you get as many object allocations as many times you run the method the lambda's in. Note that if this is only done during setup, like in onCreate, this will mean just 1 object allocation as well.

So you may get:

  • 0 extra allocations, if your listeners are methods of your already existing class (Fragment or Activity).
  • 1 extra allocation if you use a lambda that doesn't capture anything.
  • N extra allocations if you use a lambda that captures something, N being the number of times you run the code where the lambda is used, which could be 1 if this is only during initialization.
  • N extra allocations if you use an anonymous inner class, as there's no optimization there for non-capturing classes. Again, this may actually be 1 in a lot of cases.

Even though using methods inside the existing class would mean 0 additional allocations, I wouldn't go with that approach for performance - any gains will probably be completely unnoticeable. Go with the solution that's more readable and maintainable for you instead.

Ad
source: stackoverflow.com
Ad