Kotlin(run,apply)陷阱

前一段时间跟一个朋友聊天聊到run,apply里面有一个隐蔽的陷阱,记录下来,前车之鉴,后车之师

泛型T

首先我们先看一下apply的源码

fun T.apply(block: T.() -> Unit): T { block(); return this }

这个泛型T可以为null,也就是说null也被赋予了apply的方法

null.apply{
    System.out.println("null apply")
}

在IDE里面null是没有.apply方法提示的,可是在kotlin编译里面是可以通过

null埋藏的陷阱

由于null也有apply方法,所以对一个可空的对象(A)进行apply的话,block块是被执行的。如果A的方法与全局方法有重名的时候就会调用全局的方法。

open class A {
    override fun toString(): String {
        return "this is A"
    }

    open fun methodPrint() {
        System.out.println("A print")
    }
}

class B {
    override fun toString(): String {
        return "this is B"
    }

    fun methodPrint() {
        System.out.println("B print")
    }

    fun print() {
        val a: A? = null
        System.out.println(a.apply {
            methodPrint()
            System.out.println("a.apply:" + toString())
        })
    }
}

输出

B print
a.apply:null
null

如果B还有继承关系那么就更加的隐蔽

class C : A(){
    override fun toString(): String {
        return "this is C"
    }

    fun print() {
        val a: A? = null
        System.out.println(a.apply {
            methodPrint()
            System.out.println("a.apply:" + toString())
        })
    }
}

输出

A print //此处打印的是C类的methodPrint()方法继承于A
a.apply:null
null

T泛型的扩展

利用泛型的扩展我可以封装一个nullwork方法,就是一个对象为空时执行nullblock,如果对象不为空时执行noNullblock。

inline fun  T?.nullWork(noNull: (T) -> R, isNull: () -> R): R {
    return this?.let {
//这里还有一个坑,就是如果block返回null还是会执行isNull()方法所以要使用return
        return noNull(it) 
    } ?: isNull()
}

总结

陷阱的关键点有2个

  1. null对象有apply方法导致null对象的apply的block块是被执行的
  2. blcok块可以调用全局方法

前车之鉴,后车之师,大家共勉。

你可能感兴趣的:(Kotlin(run,apply)陷阱)