首页 > 解决方案 > 与匿名内部类相比,Kotlin lambda 的表现如何?

问题描述

Android Studio 建议用 lambda 替换匿名内部类。

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

反编译的Java代码:

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");
    }
}));

在 lambda 之前,为了避免为设置 OnClickListener 的每个视图创建新对象,最好让 Activity/Fragment 实现View.OnClickListener接口或使用Butterknife@OnClick注解。

如下所示的 lambda 性能会有多大不同?

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

反编译的Java代码:

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

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

在 lambda 的情况下,我看不到Log.d("MY_TAG", "textView clicked in lambda")反编译的代码。

标签: androidlambdakotlinonclicklistener

解决方案


lambda 的性能至少与创建匿名内部类一样好。

  • 如果它没有捕获任何引用,编译器会将其作为优化措施使用的任何类中的单例。这就是您的情况,因为您的侦听器的内容不引用 lambda 之外的任何内容。(这个单例实例是null.INSTANCE试图引用的,反编译器只是无法解析为 lambda 生成的类的名称。)所以在这种情况下,lambda 的成本只是 1 个对象分配。

  • 如果您的 lambda 确实捕获了某些内容,例如:

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

    ...然后,每当您设置侦听器时,都必须分配一个新实例,因为这些实例必须存储对变量的引用,每次创建它们时这些引用都可能不同。在这种情况下,您获得的对象分配次数与运行 lambda 所在的方法的次数一样多。请注意,如果这仅在设置期间完成,例如 in onCreate,这也意味着只有 1 个对象分配。

所以你可能会得到:

  • 0 额外分配,如果您的侦听器是您已经存在的类(片段或活动)的方法。
  • 如果您使用不捕获任何内容的 lambda,则额外分配 1 个。
  • 如果您使用捕获某些内容的 lambda,则 N 额外分配,N 是您运行使用 lambda 的代码的次数,如果仅在初始化期间,则可能为 1。
  • 如果您使用匿名内部类,则 N 额外分配,因为非捕获类没有优化。同样,在很多情况下,这实际上可能是 1。

即使在现有类中使用方法将意味着 0 个额外分配,但我不会采用这种方法来提高性能 - 任何收益都可能完全不明显。改用对您来说更具可读性和可维护性的解决方案。


推荐阅读