Kotlin的真实代码提供了对预定义扩展函数的更深入的理解
包括以下知识点:函数类型、扩展函数、带有接收者的lambda、apply()、also()、let()、安全调用运算符、操作符号Elvis。
apply()
函数apply()
在第一篇文章中已经提到过。这次结合实际代码进行更深入的讲解。
要在 Android 中组合多个动画,您可以使用 AnimatorSet
。使用apply()
可以在构建复合动画的代码中重复减少对象名称,使得所有语义构建一目了然:
val span = 300
AnimatorSet().apply {
playTogether(
ObjectAnimator.ofPropertyValuesHolder(
tvTitle,
PropertyValuesHolder.ofFloat("alpha", 0f, 1.0f),
PropertyValuesHolder.ofFloat("translationY", 0f, 100f)).apply {
interpolator = AccelerateInterpolator()
duration = span
},
ObjectAnimator.ofPropertyValuesHolder(
ivAvatar,
PropertyValuesHolder.ofFloat("alpha", 1.0f, 0f),
PropertyValuesHolder.ofFloat("translationY", 0f,100f)).apply {
interpolator = AccelerateInterpolator()
duration = span
}
)
start()
}
复制代码
同时为tvTitle和ivAvatar控件中创建透明度和位移动画,并设置动画时间和插值器。代码中没有多个 上面的代码中,lambda函数体除了访问之外,还紧跟在 所以AnimatorSet
对象和多个 Animator
对象,这一切都归功于 apply()”:.()
与 接受 lambda 作为参数。语义是: 将 lambda 应用于对象对象 ,其中 lambda 是一个特殊的 lambda,称为 lambda,带有接收器。这是 kotlin 特有的,在 Java 中没有。 带有接收者的 lambda 函数体不仅可以访问类成员,还可以访问接收者的所有非私有成员。这个特点是其魅力的关键。 (这个特性也非常适合构建DSL,下一篇文章会提到)
apply()
外部的。变量 span
,还可以访问 playTogether()
和 start()playTogether()
和 动画器的start()。硒。 (可以在这两个函数前面加上this
,去掉会更简洁)。 object.apply()
的另一个特点是,它在对object对象执行某些操作后返回object对象本身。apply()
适合“构造一个对象后,需要调用一些对象方法来准备并最终返回一个对象实例” let() let( )apply()
类似,但由于以下两个区别,应用场景与
- 它接受一个普通的lambda 作为参数。
- 它返回一个 lambda 值作为返回值。
项目中有一个场景:启动一个Fragment,传递bundle类型参数。如果持续时间值不为0,则显示A,否则显示B。三种常用做法爱()
class FragmentA : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let { arg ->
//调用对象方法
arg.getBundle(KEY)?.takeIf { it[DURATION] != 0 }?.let { duration ->
//将对象作为参数传递给另一个函数
showA(duration)
} ?: showB()
}
}
}
复制代码
class FragmentA : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let { arg ->
//调用对象方法
arg.getBundle(KEY)?.takeIf { it[DURATION] != 0 }?.let { duration ->
//将对象作为参数传递给另一个函数
showA(duration)
} ?: showB()
}
}
}
复制代码
。在 kotlin 中,如果只有一个 lambda 参数,则可以省略参数声明,使用 class FragmentA : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let { arg ->
//调用对象方法
arg.getBundle(KEY)?.takeIf { it[DURATION] != 0 }?.let { duration ->
//将对象作为参数传递给另一个函数
showA(duration)
} ?: showB()
}
}
}
复制代码
class FragmentA : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let { arg ->
//调用对象方法
arg.getBundle(KEY)?.takeIf { it[DURATION] != 0 }?.let { duration ->
//将对象作为参数传递给另一个函数
showA(duration)
} ?: showB()
}
}
}
复制代码
listOf(
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
).forEach {resId->
BitmapFactory.decodeResource(
resources,
resId,
BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_4444
inMutable = true
}
).also {
//存储逻辑
bitmap -> imgList.add(bitmap)
}.also {
//显示逻辑
ivImg.setImageResource(it)
}
}
复制代码
我们走吧()
:let()
会和安全电话运营商一起使用吗?
,即。语义是: 如果对象不为空,则对该对象执行一些操作。操作可以调用方法,或者将其作为参数传递给另一个函数 和 apply()
相比之下,因为 apply()
通常用于构造新对象。 (let()
用于现有对象)。新创建的对象不能为空,所以没必要?
,而且就使用习惯而言,apply()
之后的Lambda通常只是调用对象方法,而不传递对象作为另一个函数的参数(虽然这也可以做到,只需传递 this
)let( )
也会与运算符 ♽ 组合? 实现空值处理。当调用let()
的对象为空时,lambda中的逻辑将不会被执行。如果需要指定此时要执行的逻辑,可以使用?:
let()
嵌套时,明确指定 ambi的lambda参数名。 是 it
来引用它。但是当附加 lambda 时,is
的方向不清楚。因此,代码中用arg
来表示这是一个Fragment参数,用duration
来表示这是一个Bundle中的duration。
除了上述用法之外,还可以使用 有这样一个项目:为某个界面上的所有点击事件添加一个数据埋点。 当然,你可以将埋入逻辑分散在各个控件的 在函数式编程中,将函数视为值。您可以将函数作为值传递,也可以独立声明函数并将其存储在变量中,但最常见的还是直接声明并将其作为参数传递给函数。 该类的目的是将自定义的点击响应逻辑存储在函数类型变量中,并在发生点击时应用此逻辑。 在扩展函数中,您可以像任何其他类成员函数一样访问类属性和方法(私有和受保护成员除外)。本例中调用 最后,在原始点击事件响应函数中实现嵌入逻辑,并调用存储在函数类型变量中的自定义点击响应逻辑。 与 在项目中,有一个接口应该在初始化时加载一系列图像并将它们存储在列表中: 在这种情况下使用 因为 作者:唐子轩let()
作为变换函数
,如 地图()
运算符。因为 let()
将 lambda 值作为返回值。 OnClickListener
中,但如果你想通过埋入逻辑进行集成控制,可以使用以下解决方案: ❀❀ 点击响应类逻辑class OnClickListenerBuilder {
//'点击响应逻辑'
var onClickAction: ((View) -> Unit)? = null
//'为点击响应逻辑赋值的函数'
fun onClick(action: (View) -> Unit) {
onClickAction = action
}
}
复制代码
OnClickListenerBuilder
定义了一个函数类型为onClickAction
的成员变量,类型为() -> 相同单位? 看。 OnClickListener
中的函数void onClick(View view)
完全相同,即输入View
,返回空值。该成员变量的值可以为空,因此在原始函数类型(View) -> Unit
之外多了一个括号和一个问号。 //'定义扩展函数'
fun View.setOnDataClickListener(action: OnClickListenerBuilder.() -> Unit) {
setOnClickListener(
OnClickListenerBuilder().apply(action).let { builder ->
View.OnClickListener { view ->
//'埋点逻辑'
Log.v(“ttaylor”, “view{$view} is clicked”)
//'点击响应逻辑'
builder.onClickAction?.invoke(view)
}
}
)
}
//'在界面中使用扩展函数为控件设置点击事件'
btn.setOnDataClickListener {
onClick {
Toast.makeText(this@KotlinExample, “btn is click”, Toast.LENGTH_LONG).show()
}
}
复制代码
setOnDataClickListener()
for ❀❀。扩展函数是类的成员函数,但在类体之外定义。这种定义的优点是可以随时随地向类添加功能。 setOnClickListener()
来控制设置点击事件。OnClickListenerBuilder
中的点击响应逻辑。代码中的OnClickListenerBuilder().apply(action)
在构建实例时将 lambda 应用于实例。这是一个带有接收器的 lambda,以及接收器 OnClickListenerBuilder
。这作为参数传递给扩展函数。当调用 setOnDataClickListener()
时,我们可以轻松地在传入 lambda 中调用 onClick()
方法,因为可以在成员 lambda 中非私有地访问接收者。这样,点击响应逻辑就存储在函数类型变量 onClickAction
中。 map()
时使用let()
。由于API限制,View.setOnClickListener()
的参数必须为View.OnClickListener
类型,所以OnClickListener 必须改为View.OnClickListener
。这可以通过调用 let()
轻松完成,因为它返回一个 lambda 值。 also()
also()
与 let()
非常相似,唯一的区别是它返回调用者本身而不是 lambda 值。价值。 apply()
相比,它也返回自己的调用者:apply()和
app中的 ly() 和 mb also()
以普通 lambda 传递。所以在lambda函数体中,前者通过this
引用调用者,后者通过it
引用调用者(如果参数名没有定义,则默认为它) .) apply()
更多地用于构建新对象并执行操作,而also()
更多地用于向现有对象添加操作。 listOf(
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
).forEach {resId->
BitmapFactory.decodeResource(
resources,
resId,
BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_4444
inMutable = true
}
).also { bitmap -> imgList.add(bitmap) }
}
复制代码
let()
没有任何问题。但如果你还需要显示解析后的图像,最好使用also()
:listOf(
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
).forEach {resId->
BitmapFactory.decodeResource(
resources,
resId,
BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_4444
inMutable = true
}
).also {
//存储逻辑
bitmap -> imgList.add(bitmap)
}.also {
//显示逻辑
ivImg.setImageResource(it)
}
}
复制代码
also()
,你可以自己调用它。 使用also()
来拆分不同类型的逻辑,代码更容易理解和修改。这个例子的逻辑比较简单,只有一句话,所以组合起来并没有什么问题。 知识点总结
扩展功能
是可以在类之外添加新功能的功能。在扩展函数体中,可以访问类成员(私有和受保护修饰的成员除外)使用 lambda 接收者
是一种特殊类型的 lambda,可以访问函数体内的非私有接收者成员。可以理解为一个接收者扩展函数,只不过这个扩展函数没有函数名。 apply()
also()
let()
是系统定义的扩展函数。用于简化代码并减少重复的对象名称。 ?.
被称为安全电话接线员。如果 object?.fun()
中的对象为空,则不会调用 fun()
。 ?:
被称为 Elvis 运算符,为 null 提供标准逻辑,funA() ?: funB()
,如果 funA() 返回非 null 值,它将被执行回。该值用作所有表达式的返回值,否则执行 funB() 并使用返回值。
链接:https://juejin.im/post/5d061caef265da1b8f1ac00a
来源:掘金商业转载请联系作者授权。非商业转载请注明出处。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。