Kotlin中的引用

前言

Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言

img.jpg

引用

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 知识整理

你可能感兴趣的:(Kotlin中的引用)