前言
Kotlin
是一种在Java
虚拟机上运行的静态类型编程语言,被称之为Android
世界的Swift
,在Google
I/O2017中,Google
宣布Kotlin
成为Android
官方开发语言
什么是扩展函数
扩展函数就是可以在类的外部声明额外的类的函数,并且可以扩展系统函数,让使用更方便,代码更简洁
举个例子,TextView是我们常用的组件,通常情况下我们会在布局或者代码中调用setText
函数,对其进行内容设置,当我们需要当设置内容为空的时候我们显示NaN
,通常我们可以这样做
fun setTextOptional(textView: TextView, text: String?) {
textView.text = if (text.isNullOrEmpty()) {
"NaN"
} else {
text
}
}
setTextOptional(textView, "complete")
setTextOptional(textView, null)
setTextOptional(textView, "inprogress")
上述的处理方式可以达到我们的需求,我们之所以这么做是因为TextView
本身并不支持"NaN"的处理,如果我们可以扩展TextView
给它添加一个setTextOptional
函数那就更好了,扩展函数就可以做到
扩展函数的定义
fun receiverType.functionName(params){
body
}
扩展函数一般定义在顶层位置,一般用于功能的扩展,不会影响原来类的结构,不用继承或者装饰就直接可以扩展类的内容
receiverType
表示要扩展的目标类
functionName
表示扩展函数名称
params
扩展函数的参数
基于上述的案例,我们需要扩展TextView
的功能,那么就可以这样定义,函数内部可以使用this
拿到当前的调用对象,当然this
也可以省略
fun TextView.setTextOptional(text: String?) {
this.text = if (text.isNullOrEmpty()) {
"NaN"
} else {
text
}
}
然后就可以直接这样,更加直观的使用,也更加简单
textView.setTextOptional("complete")
textView.setTextOptional(null)
textView.setTextOptional("inprogress")
上述代码其实会编译成这样的java代码
public static final void setTextOptional(@NotNull TextView $this$setTextOptional, @Nullable String text) {
$this$setTextOptional.setText((CharSequence)(var6 ? "NaN" : text));
}
扩展函数的静态解析
使用官方的例子,子类与父类扩展了相同的函数,调用时不会根据运行时具体类型去判断,而是由函数调用位置的表达式类型所确定的
open class Shape
class Rectangle: Shape()
fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"
fun printClassName(s: Shape) { //此处定义s的类型为Shape
println(s.getName())
}
printClassName(Rectangle()) //Shape
扩展函数与成员函数的冲突
如果扩展函数与成员函数拥有相同的函数签名,则总是取成员函数,应该使用重载或者不同的命名方式去避免这种问题的出现
class Example {
fun printFunctionType() { println("Class method") }
}
fun Example.printFunctionType() { println("Extension function") }
Example().printFunctionType()
可空的接受者
前面我们使用到了isNullOrEmpty
函数,它可以使用空对象去调用,因为在这里它声明了CharSequence调用对象可以为空
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.length == 0
}
可空的接受者,可以减少外部调用的额外判断
fun TextView?.setTextOptional(text: String?) {
this?.text = if (text.isNullOrEmpty()) {
"NaN"
} else {
text
}
}
使用BindingAdapter
在布局中扩展函数
上述我们扩展了TextView的函数,让其在代码中可以直接调用扩展函数进行额外的功能,那么很多时候我们需要在布局中进行内容的绑定,就像android:text="Login"
一样,我们需要使用到DataBinding
提供的BindingAdapter
对扩展函数进行注解即可在布局中使用扩展函数
@BindingAdapter("textOptional")
fun TextView?.setTextOptional(text: String?) {
this?.text = if (text.isNullOrEmpty()) {
"NaN"
} else {
text
}
}
在布局中使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="status"
type="com.mike.loginmvvm.login.ui.Response" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
app:textOptional="@{status.data}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</layout>
class LoginActivity : DaggerAppCompatActivity() {
val binding: AtctivityLoginBinding by ViewDelegate(R.layout.atctivity_login)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.setStatus(Response("1", ""))
}
欢迎关注Mike的简书
Android知识整理