美文网首页
Kotlin中的引用

Kotlin中的引用

作者: 盛世光阴 | 来源:发表于2021-06-10 01:08 被阅读0次

    前言

    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<Any?, Any?>(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<out R> : KProperty<R>, () -> R {}
    
    fun test(value:()->Int){}
    test(this::test)
    

    属性引用的使用

    String::length
    编译后
    KProperty1 var10000 = TestReference$call$1.INSTANCE;
    

    会生成一个KProperty1 对象

    public actual interface KProperty1<T, out R> : KProperty<R>, (T) -> R {}
    

    String::length等价于

    (Strring) ->{ str
       returm str.length
    }
    

    所以可以写为

    listOf<String>("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<String, Int>) : 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中的引用

          本文链接:https://www.haomeiwen.com/subject/ycdfeltx.html