android - 与匿名内部类相比,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")
反编译的代码。
解决方案
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 个额外分配,但我不会采用这种方法来提高性能 - 任何收益都可能完全不明显。改用对您来说更具可读性和可维护性的解决方案。
推荐阅读
- javascript - 什么是解析作为逗号分隔的键 = 值对列表的 javascript 字符串的简洁方法?
- node.js - 如何识别 websocket 连接来自哪个会话?
- powershell - 是否可以使用变量的值作为新的变量名?
- android - 如何使用 SearchBar 过滤 FirebaseRecyclerOptions
- sql - 在 UNION ALL 视图创建中使用 JOIN
- android - 如何覆盖 BottomSheetDialogFragment 的后退按钮?
- java - 如何写入和读取同一个文件
- reactjs - 如何在 Apollo 客户端中手动触发多个查询?
- botframework - 如何让 notifySuccess 发送验证码?
- google-cloud-spanner - Google Cloud Spanner 表名和列名何时以及为何区分大小写?