前言
Kotlin
是一种在 Java
虚拟机上运行的静态类型编程语言,被称之为 Android
世界的Swift
,在Google
I/O 2017中,Google
宣布 Kotlin
成为 Android
官方开发语言
引用
Kotlin 中的引用有不同的使用方式有 函数引用、属性引用、类引用、构造函数引用、扩展函数引用、绑定引用等
函数引用
可以通过::
引用其他函数作为高阶函数的参数
对象::函数名
类名::函数名
将函数转换为lambda的格式
首先随便定义一个函数
class Test {
fun print(str: String) {
}
}
这个函数有一个String参数,无返回值,将他转换为lambda表达式的形态就是
(String) -> Unit
任何函数都可以转换为lambda的形态
(String) -> Int
(String,String) -> String
如何使用函数引用
定义一个高阶函数 test
class Test {
fun print(str: String) {
print(str)
}
fun test(value:(String) -> Unit){
value("test")
}
}
当我们调用的时候需要个给value
传入一个参数为String
,返回值为Unit
的函数,我们一般可以直接在调用处声明一个对应的lambda
,也可以引用符合此函数类型的其他函数
fun main(){
test {
}
test(this::print) //函数引用
}
这种写法编译之后就会变成
fun print(str: String) {
print(str)
}
fun test(value:(String) -> Unit){
value("test")
}
编译后
public final void print(String str){
print(str)
}
public final void test(@NotNull Function1 call) {
}
函数引用位置
test(this::print)
编译后
test1(object : Function1(this) {
operator fun invoke(p1: String) { //根据所引用的函数所自动生成的函数
(this.receiver as TestReference).test(p1)
}
})
函数引用也可以直接执行,因为引用本质上也是一个函数
class Account {
fun test() {}
fun call() {
val a1 = Account::test
a1(Account())
val a2 = Account()::test
a2()
}
}
函数引用的使用场景
引用现有的函数实现,不用再创建自己的实现
help.setOnClickListener { }
可以将响应事件的Lambda抽离成函数,然后进行引用
btnLogin.setOnClickListener(::onClick) //this可以省略
fun onClick(view:View){}
属性引用
属性引用与函数引用用法相同,只不过目标是属性
class LoginActivity : DaggerAppCompatActivity() {
val test = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e("mike", "${this::test}")
Log.e("mike", ::test.name)
Log.e("mike", "${::test.get()}")
}
}
mike: val com.mike.loginmvvm.login.ui.LoginActivity.test: kotlin.Int
mike: test
mike: 1
属性引用这里主要是编译生成用到了KProperty相关,this::test
被编译成了一个持有引用对象this的KProperty0
对象
this::test
编译后
KProperty0 t1 = new TestReference$call$t1$1((TestReference)this)
再看KProperty0的声明,在上面的案例中他也可以当做函数() -> Int
使用
public actual interface KProperty0 : KProperty, () -> R {}
fun test(value:()->Int){}
test(this::test)
属性引用的使用
String::length
编译后
KProperty1 var10000 = TestReference$call$1.INSTANCE;
会生成一个KProperty1
对象
public actual interface KProperty1 : KProperty, (T) -> R {}
String::length
等价于
(Strring) ->{ str
returm str.length
}
所以可以写为
listOf("1", "12", "123").map(String::length)
输出[1,2,3]
类引用
获取KClass
以及Class
对象,是我们使用最多的引用方式
- 类名::class
- 类名::class.java
- 对象::class
- 对象::class.java
构造函数引用
引用类的构造函数
class Test {
constructor() {}
constructor(str: String) {}
fun test1(value: () -> Test) {}
fun test2(value: (String) -> Test) {}
fun call() {
test1(::Test) //引用构造函数 constructor() {}
test2(::Test) //引用构造函数 constructor(str: String) {}
}
}
可以引用构造函数,作为函数的入参,引用哪个构造函数取决于函数的Lambda样式
构造函数引用的使用
进行数据转换中,对象元素的映射创建
class Person(val name: String, val age: Int) {
constructor(map: Pair) : this(map.first, map.second)
}
//引用构造函数,进行数据转换
listOf("张三" to 18, "李四" to 19, "王五" to 20, "Mike" to 21).map(::Person)
扩展引用
扩展函数(属性)也可以被引用
- 类名::扩展函数(属性)名
- 对象::扩展函数(属性)名
fun TextView.person(person: Person) {
text = if (person.name.isEmpty()) "" else person.name
}
fun call(textView: TextView){
val per = TextView::person
per(textView,Person("",1))
val per1 = textView::person
per1(Person("",1))
}
val bindPerson:TextView.(Person) ->Unit = TextView::person
isInitialized
扩展属性判断成员是否已经初始化
被lateinit修饰的变量需要在使用时做判断,判断是否已经初始化,未初始化使用将会抛出异常
lateinit var data:Person
需要使用到 isInitialized
去进行判断,先看下其定义
/**
* Returns `true` if this lateinit property has been assigned a value, and `false` otherwise.
*
* Cannot be used in an inline function, to avoid binary compatibility issues.
*/
inline val @receiver:AccessibleLateinitPropertyLiteral KProperty0<*>.isInitialized: Boolean
get() = throw NotImplementedError("Implementation is intrinsic"
- 首先它不能使用到
inline function
中 - 它是
KProperty0
的扩展属性,所以需要KProperty0
对象才能调用 - 返回值此属性是否已经初始化
拿到data
的引用,也就是KProperty0
,然后调用isInitialized
if(::data.isInitialized){
}
欢迎关注Mike的
Android 知识整理