Kotlin特性之 - 扩展函数

1. 扩展函数的写法

// fun 关键字 + 要扩展的类名 + 点号 + 方法名 + 方法属性
fun String.method1(i: Int) {
  ...
}
这里要扩展函数的类名叫做Receiver(接受者),也就是谁可以去调用它.

在声明一个函数的时候在函数名的左边写个类名再加个点,你就能对这个类的对象调用这个函数了。这种函数就叫扩展函数,Extension Functions。就好像你钻到这个类的源码里,改了它的代码,给它增加了一个新的函数一样。虽然事实上不是,但用起来基本一样。

Top Level扩展函数

package com.zhf

fun String.method1(i: Int) {
  ...
}

// 在使用的地方调用就可以了
"rengwuxian".method1(1)
Top Level扩展函数是属于谁呢?

它既是Top Level函数,又是前缀类的扩展函数, 它的作用域是package包内部, 他的Receiver是点左侧的类

看了很多教程和解释, 自己理解其实这个函数就是个顶层函数,它只属于它所在的 package, 不过他只能被Receiver(接收者)使用, 就是上面代码里的String,

虽然说它是个 Top-level Function,不属于任何类——确切地说是,不是任何一个类的成员函数——但我要限制只有通过某个类的对象才能调用你。这就是扩展函数的本质。

成员扩展函数

写在类里的扩展函数 , 你可以在这个类里调用这个函数,但必须使用那个前缀类的对象(Receiver)来调用它

class Example {
  fun String.method2(i: Int) {
    ...
  }
   "rengwuxian".method2(1) // 可以调用
}
成员扩展函数是属于谁呢? —

它既是外部类的成员函数,又是前缀类的扩展函数. 它的作用域是类内部, 他的Receiver是点左侧的类

这个「属于谁」其实有点模糊的,我需要问再明确点:
它是谁的成员函数?当然是外部的类的成员函数了,因为它写在它里面嘛,
那函数名左边的是什么?它是这个函数的 Receiver,对吧?也就是谁可以去调用它。
所以它既是外部类的成员函数,又是前缀类的扩展函数。

指向扩展函数的引用

函数是可以使用双冒号被指向的, 双冒号加方法名 指向的并不是函数本身,而是和函数等价的一个对象,
我们通常可以把这个「指向和函数等价的对象的引用」称作是「指向这个函数的引用」

顶层扩展函数的 指向扩展函数的引用

fun String.method1(i: Int) {
}
String::method1 

上面 String::method1 就是指向扩展函数的引用, 它其实是可以作为对象来传递和使用的,当然它也可以作为参数,传递给方法, 这就是高阶函数了
注意: 以下代码中 method3 和 method4 为什么都可以 传如String 的函数引用 下文会说到

	fun String.method1(i: Int): String {
	}
	class Example {
		val a = String::method1
		val b = a

        method3(String::method1)
        method3(b)
        
        method4(String::method1)
        method4(b)

		fun method3(method: String.(Int) -> String) {}
		fun method4(method: (String, Int) -> String) {}
	}
 

成员扩展函数的 指向扩展函数的引用 ( *** 不存在)

一个成员函数怎么引用:类名加双冒号加函数名对吧?扩展函数呢?也是类名加双冒号加函数名对吧?只不过这次是 Receiver 的类名。那成员扩展函数呢?还用类名加双冒号加函数名呗?但是……用谁的类名?是这个函数所属的类名,还是它的 Receiver 的类名?这是有歧义的,所以 Kotlin 就干脆不许我们引用既是成员函数又是扩展函数的函数了,一了百了。


class Extensions {
  fun String.method1(i: Int) {
    ...
  }
  fun method2(i: Int) {
	...
  }
  String::method1 // 报错
}
class Example {
	Extensions ::method1 // 报错
	Extensions ::method2 
}

把扩展函数的引用赋值给变量

扩展函数的引用也可以赋值给变量, 然后你再拿着这个变量去调用,或者再次传递给别的变量,都是可以的
跟普通函数的引用一样,扩展函数的引用也可以被调用,直接调用或者用 invoke() 都可以,不过要记得把 Receiver 也就是接收者或者说调用者填成第一个参数

	
	val a: String.(Int) -> Unit = String::method1
	"zhf".a(1)
	a("zhf", 1)
	a.invoke("zhf", 1)

有无 Receiver 的变量的互换

在 Kotlin 里,每一个有 Receiver 的函数——其实就是成员函数和扩展函数——它的引用都可以赋值给两种不同的函数类型变量:一种是有 Receiver 的,一种是没有 Receiver 的:
有 Receiver 的函数和没有 Receiver 的函数,在写法上就是把 Receiver 放在没有Receiver 的第一个参数

	val a: String.(Int) -> Unit = String::method1
	val b: (String, Int) -> Unit = String::method1
	
	val c: String.(Int) -> Unit = b
	val d: (String, Int) -> Unit = a

扩展 (可直接跳过)

下面三段代码,第一段代码里的 clz / clz1/ clz2/ clz3 这四个对象是一个对象吗? 他们之间有何不同呢?

  1. LoginActivity::class 和 mLoginActivity::class 这两个是相同的对象,
    类名或者类的对象加双冒号,代表 指向扩展函数的引用 所以 clz1和clz2 是相同的对象
  2. LoginActivity::class 返回的是什么呢 ? 在kotlin中的Class与Java不同,
    kotlin中有一个自己的Class叫做KClass 双冒号加class 返回的就是 KClass
  3. mLoginActivity::class.java 和 mLoginActivity.javaClass 这两个是相同的对象 .
    第二段代码,是KClass 的一个扩展函数, 他的返回值是 Class 第三段代码,是T 的一个扩展函数, T就是某个函数名 他的返回值是 Class

结论就是: clz != clz1 == clz2 ==clz3

       代码一 
        val clz = LoginActivity::class
        val clz1 = LoginActivity::class.java
        val clz2 = mLoginActivity::class.java
        val clz3 = mLoginActivity.javaClass
  
        val intent = Intent();
        intent.setClass(mContext, clz1 )
        startActivity(intent)
代码二
	@Suppress("UPPER_BOUND_VIOLATED")
	public val <T> KClass<T>.java: Class<T>
	    @JvmName("getJavaClass")
	    get() = (this as ClassBasedDeclarationContainer).jClass as Class<T>
代码三
	public inline val <T : Any> T.javaClass: Class<T>
	    @Suppress("UsePropertyAccessSyntax")
	    get() = (this as java.lang.Object).getClass() as Class<T>

本文摘录自: https://rengwuxian.com/kotlin-extensions/

上一篇:Oracle:select 或 inactive 会话语句产生锁?


下一篇:数据库会话数量过多,定期清理inactive会话