kotlin扩展函数

kotlin中有个很好用的功能叫扩展,它的作用是对已有的类额外添加函数和属性并且不需要修改类源码也不需要创建子类。

例如:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

这是一个以Float类型作为receiver的扩展函数,receiver的定义就是说一个扩展函数需要明确指定哪个类的实例可以调用这个扩展函数,而函数中的this对象会指向函数的调用者即一个未知的Float实例。它的调用方式也很简单:

1f.getMax(2f)

这样,在你的代码中的某些作用域下的Float类型实例就可以使用getMax这个函数。注意这里指的是某些作用域下,说明扩展函数的作用域是有限制的,只不过这个限制可大可小:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        1f.getMax(2f)
    }
    ...
}

如上所示getMax方法的声明被放在了MainActivity2类的外部,我们知道kotlin有顶层这样一个概念,这里的getMax方法不是任何一个类的成员方法,它就属于顶层方法。顶层方法的作用域是整个包即包下的所有类或代码块都可以使用getMax方法。当然,扩展函数也可以写在某个类里面,作为这个类的成员函数存在:


class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        1f.getMax(2f)
    }

    fun Float.getMax(v1: Float): Float {
        return if (this > v1) this else v1
    }
    ...
}

由于getMax方法是MainActivity2的成员方法,那么它就只能在MainActivity2类中使用。这里的getMax就被同时赋予了两个角色:MainActivity2的成员函数、Float的扩展函数。

我们知道在kotlin中函数可以使用双冒号被指向为一个函数类型的对象,即:

(Int::toString)(3)
Int::toString.invoke(3)
3.toString()

这三句代码功能都是一样的,toString是Int类下的一个普通函数,通过::将toString函数指向为一个函数类型的对象再通过invoke方法来执行源函数中代码逻辑。同样的,扩展函数也可以被双冒号进行指向,但是当扩展函数是成员函数时这样的指向是不被kotlin允许的,因为你根本无法确定你要指向的到底是这个函数所属的类还是receiver的类,所以kotlin干脆就直接干掉不允许下面这样写:

kotlin扩展函数_第1张图片

 可以看到,两处指向都被编译器报红了。但是对于顶层的扩展函数是可以被指向的,因为它不隶属于任何一个类:

kotlin扩展函数_第2张图片

 可以看到invoke方法的编译是正常的,同时要注意一点扩展函数的调用者会作为第一个参数传递给invoke方法。

既然顶层扩展函数可以被指向为一个函数类型的对象,那么它也一定可以被赋值为一个函数类型的对象:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        val a: Float.(Float) -> Float = Float::getMax
        1f.a(2f)
        a(1f, 2f)//等价于1f.a(2f)
        a.invoke(1f, 2f)//等价于1f.a(2f)
    }
    ...
}

可以看到的是不论顶层扩展函数的实例被如何指向或赋值,他在调用时方法的第一个参数永远是函数的调用者,这是kotlin严格规定的。同时,顶层扩展函数的实例还可以被赋值给无receiver的函数类型的变量:

val b: (Float, Float) -> Float = Float::getMax

这样写是合法的,你只需要知道这样写的时候第一个参数它所扮演的角色是什么就可以了。并且,a和b这两个函数类型的变量是可以相互赋值的:

val a: Float.(Float) -> Float = Float::getMax
val b: (Float, Float) -> Float = Float::getMax
val c: Float.(Float) -> Float = b
val d: (Float, Float) -> Float = a

既然一个有receiver的函数变量可以被赋值为一个无receiver的函数变量,那同样的,一个无receiver的函数也可以被双冒号指向为一个有receiver的函数引用:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

fun getMin(v1: Float, v2: Float): Float {
    return if (v1 < v2) v1 else v2
}

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        val a: Float.(Float) -> Float = Float::getMax
        val b: (Float, Float) -> Float = Float::getMax
        val c: Float.(Float) -> Float = b
        val d: (Float, Float) -> Float = a
        val e: (Float, Float) -> Float = ::getMin
        val f: Float.(Float) -> Float = ::getMin
    }
    ...
}

可以看到getMin方法是一个普通方法没有receiver,但f是一个以Float作为receiver的扩展函数对象,这样的赋值也是被允许的。这样就可以把一个非扩展函数通过重新指向为有receiver的扩展函数来进行调用:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

fun getMin(v1: Float, v2: Float): Float {
    return if (v1 < v2) v1 else v2
}

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        val a: Float.(Float) -> Float = Float::getMax
        val b: (Float, Float) -> Float = Float::getMax
        val c: Float.(Float) -> Float = b
        val d: (Float, Float) -> Float = a
        val e: (Float, Float) -> Float = ::getMin
        val f: Float.(Float) -> Float = ::getMin
        2f.getMin(3f)//编译报错
        2f.f(3f)//运行正常
    }
    ...
}

同样的你也可以把一个扩展函数通过重新指向为无receiver的非扩展函数来进行调用,只不过这会收窄函数功能的多样性:

fun Float.getMax(v1: Float): Float {
    return if (this > v1) this else v1
}

fun getMin(v1: Float, v2: Float): Float {
    return if (v1 < v2) v1 else v2
}

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        val a: Float.(Float) -> Float = Float::getMax
        val b: (Float, Float) -> Float = Float::getMax
        val c: Float.(Float) -> Float = b
        val d: (Float, Float) -> Float = a
        val e: (Float, Float) -> Float = ::getMin
        val f: Float.(Float) -> Float = ::getMin
        2f.getMax(3f)//运行正常
        2f.b(3f)//编译报错
    }
    ...
}

至于扩展属性,他在声明上和扩展函数遵循一样的规则,都必须明确receiver,但是和普通成员属性不同的是他不允许有初始化容器,只能通过get/set去使用:

val MyClass.code: Boolean = false //报错,不能有初始化容器
val MyClass.code: Boolean
    get() = code.isEmpty()//正确

你可能感兴趣的:(android,kotlin,android)