Kotlin特性之 - 扩展函数/指向函数的引用/扩展属性

1. 扩展属性

kotlin允许动态为类扩展属性,扩展属性是通过添加get、set方法实现,没有幕后字段(filed)。
扩展属性也没有真的为该类添加了属性,只能说是为该类通过get、set方法计算出属性。
限制:①扩展属性不能有初始值;
②扩展属性不能用filed关键字访问幕后字段;
③val必须提供get方法,var必须提供get和set方法。

var类型

	class ExtensionTest(var param1: String, var param2: String)
	
	var ExtensionTest.extensionParam: String
	    set(value) {
	        param1 = "param1$value"
	        param1 = "param2$value"
	    }
	    get() = "$param1-$param2"
	    
	fun main(args: Array<String>) {
	    val extensionTest = ExtensionTest("a", "b")
	    println(extensionTest.param1)//a
	    println(extensionTest.param2)//b
	    println(extensionTest.extensionParam)//a-b
	}

val类型

	val Float.dp
	  get() = TypedValue.applyDimension(
	    TypedValue.COMPLEX_UNIT_DIP,
	    this,
	    Resources.getSystem().displayMetrics
	  )
	...
	val RADIUS = 200f.dp

2. 扩展函数的写法

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

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

注意:

  1. 如果被扩展的类的扩展方法与该类的成员方法名字和参数一样,该类对象调用该方法时,调用的会是成员方法。
  2. 由于静态调用扩展方法是在编译时执行,因此,如果父类和子类都扩展了同名的一个扩展方法,引用类型均为父类的情况下,会调用父类的扩展方法。

3 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,不属于任何类——确切地说是,不是任何一个类的成员函数——但我要限制只有通过某个类的对象才能调用你。这就是扩展函数的本质。

4 成员扩展函数

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

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

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

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

5 指向扩展函数的引用

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

顶层扩展函数的 指向扩展函数的引用
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 就干脆不许我们引用既是成员函数又是扩展函数的函数了,一了百了。

	fun Extensions .method2(i: Int) {
		...
	}
	class Extensions {
	  fun String.method1(i: Int) {
	    ...
	  }
	  fun method2(i: Int) {
		...
	  }
	  String::method1 // 报错
	}
	
	class Example {
		Extensions ::method1 // 报错
		Extensions ::method2  // 方法名相同, 成员方法优先
	}

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

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

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

7 有无 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/
https://www.cnblogs.com/nicolas2019/p/10932131.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值