反射

kotlin 通过 kotlin-reflect.jar 提供对反射的支持。

KClass

通过类引用 KClass

KClass 引用了 kotlin 类(具有内省能力)。类似于 Java 的 class 。要获取一个类的 KClass,通过类型名::class获得,而对应的 Java class 则通过类型名::class.java获得:

fun main(args: Array<String>) {val c= String::class // 获取 KClassprintln(c)    // 打印:class kotlin.Stringval c2 = String::class.java // 获取 Java Classprintln(c2) // 打印:class java.lang.String
}
通过实例引用 KClass

实际上和通过类引用一样,都是使用 :: 表达式,不过前半部分从类名变成了对象名。

open class Parent
class Son:Parent()
class Daughter: Parent()fun main(args:Array<String>){val son:Parent = Son()val daughter: Parent = Daughter()println(son::class) // 打印:class com.kotlin13.Sonprintln(son::class.java) // 打印:class com.kotlin13.sonprintln(daughter::class) println(daughter::class.java)
}

可以看到虽然 对象声明时使用的是父类型,但它的 KClass 仍然是具体的子类型。此外,KClass 和 java class 的输出是一样的。

函数引用

fun addOne(x:Int): Int {return x+1
}
fun addOne(s:String):Int {return 10
}
fun main(args:Array<String>){val values = listOf(1,3,5,7)println(values.map(::addOne)) // 1.函数引用val values2 = listOf("a","b","c")println(values2.map(::addOne)) // 2.函数引用重载
}
  1. Map 函数需要用另外一个函数作为参数。这里的 ::addOne 是一个方法引用(KFunction 类型,相当于 java 的 Method,具有内省能力的函数)。因为 addOne 是顶层函数,所以类名被省略了,直接以 :: 开始。

  2. 函数引用也有重载形式,因此两个 addOne 方法到底调用哪一个,kotlin 会通过类型推断来进行判断。对于 Int 数组,map 函数传递给 addOne 函数的参数应该是 Int,所以会调用第一个 addOne。对于 String 数组,map 函数会在每个 String 上应用 addOne 函数,因此只会调用第二个 addOne 函数。

这里,::addOne 真正类型其实是 (Int)->Int 或 (String)->Int。因此可以把一个函数引用赋给一个变量:

val ref1: (Int) -> Int = ::addOne // 引用第一个 addOne
val ref2: (String) -> Int = ::addOne // 引用第二个 addOne
val ref3:String.(Int)->Char = String::get // 引用 String 的 get(Int) 方法

值得注意的是,ref3 的声明类型。由于 String.get 不是一个顶层函数,所以类名前缀不可省略。

函数组合

即将两个函数组合成一个函数。

fun <A,B,C> myCompose(f:(B)->C, g:(A)->B): (A)->C {// 一个范型函数,接收两个函数参数,返回一个函数return { x -> f(g(x)) } // 返回一个函数
}

myCompose 将两个函数调用串联起来,即首先调用 g, 将一个 A 变成 B,然后调用 f,将 B 变成 C:

g: (A) -> B

f: (B) -> C

myCompose = g + f: (A) -> (B) -> C

这里, B 其实只是一个中间结果,是可以舍弃的,舍弃后就变成 myCompose: (A) -> C。

return { x -> f(g(x)) } 一句表示返回一个 lambda 表达式(函数),这个函数接收一个 x 参数(应该是一个 A 类型,和 g 的参数相同),然后返回 f(g(x)) 的结果(根据 f 的定义,返回的 将是一个 C 类型)。

因此我们查看 myCompose 的代码可知,这个函数其实是把两步调用合并成一个一步调用,原来需要先调用 B = g(A),再调用 C = f(B) 才能得到 C,现在变成了 C = myCompose(A)。

接下来我们可以利用这个函数,将获取字符串长度和判断长度是否为偶数到操作合并成一步操作:

fun isEven(x:Int) = 0==x%2 // 判断是否偶数
fun length(s:String) = s.length // 获取字符长度,它输出类型就是 isEven 的输入类型,因此可以将它的调用结果作为isEven 的参数
fun main(args: Array<String>){val f = myCompose(::isEven, ::length) // 得到一个 (A) -> C 到函数val strings = listOf("a","ab","abc")println(strings.map(f)) // 输出 false, true, false  println(strings.filter(f)) // 输出 ab,长度为奇数的被过滤掉了
}

属性引用

形式与函数引用相同。实际上,:: 操作符不仅仅可以用于函数引用。

const val a = 3
fun main(args: Array<String>) {println(::a) // 打印属性类型: val a: koltin.Intprintln(::a.get()) // 打印属性值: 3println(::a.name) // 打印属性名:a
}

这里,::a 表示一个 KProperty 对象。通过这个 KProperty 访问指定属性的具体信息,相当于 java 的 Field。

KProperty 是一个接口,代表一个属性(val 或 var)。

注意,对于包级别的属性引用,使用的是 KProperty0 (继承自 KProperty -> KCallable)。

对于 var 属性,则使用 KMutableProperty。这个接口继承自 KProperty 但具有 set 方法。

var b = 5
fun main(args: Array<String>) {::b.set(10)println(b) // 打印 10println(::b.get()) // 打印 10
}

属性引用和函数引用很多时候是相同的,比如:

fun main(args: Array<String> ){val values = listOf("a","abc","abcd")println(values.map(String::length)) // 引用 String.length属性,打印: 1,3,4
}

map 接收一个函数函数,我们应该将一个函数引用传递给它,但这里我们实际上传递了一个属性引用,这充分说明了属性引用和函数引用本质上是相同的。同时,map 会将数组中的元素传递函数引用,但如果是属性引用,map 会将元素传递给属性引用的接收者,即变成了 it.length,这会调用数组元素的 length 方法。

如果引用的属性不是包级别的,比如:

class MyClass(val x: Int)
fun main(args: Array<String>){val x = MyClass::xprintln(x.get(MyClass(10))) // 1 打印:10}
  1. 如果属性属于具体类,那么所对应的属性引用必须依赖于具体的实例存在,因此 get 方法必须传递该类的一个实例。

通过实例获得函数/属性引用

fun main(args: Array<String>) {val str="abc"val getRef = str::get // 通过对象而非类名来引用,对比 String::getprintln(getRef(1)) // 打印:bval propRef = "test"::length println(propRef.get()) // 打印:4
}

注意,上述代码实际上等效于:

fun main(args: Array<String>) {val getRef = String::get // 通过类来引用,对比 str::getprintln(getRef("abc", 1)) // 打印:bval propRef = String::length println(propRef.get("test")) // 打印:4
}

扩展属性

定义一个扩展属性:

val String.firstChar: Charget() = this[0]

如何通过属性引用的方式打印这个 firstChar?

fun main(args:Array<String>){val str = "xyz"val x = String::firstCharprintln(x.get(str)) // 打印:x
}

实际上和属性引用是一样的。

构造方法引用

引用形式仍然是 :: 表达式。区别仅在于,构造方法引用仅用于向参数和返回值与构造方法相同的变量赋值。

class MyClass(val x: Int)
fun method(factory: (x: Int) -> B) { // factory 可以接受一个构造方法引用val myClass:MyClass = factory(10)println(myClass.x)
}
fun main(args: Array<String>) {method(::MyClass) // 向 method 传入一个构造方法引用
}

::MyClass 表示了构造方法:MyClass (val x: Int)。这个构造方法的签名和 factory 参数是一样的,接受一个 Int,返回一个 MyClass 对象。

类型参数

typeParameters 代表类型的范型参数列表:

class MyClass<K,V> {var k: K? = nullvar v: V? = null
}
fun main(args: Array<String>){val kc = MyClass::classprintln(kc.typeParameters) // 打印:[K, V]println(kc.typeParameters.size) // 打印:2println(kc.typeParameters[0]) // 打印:K
}

【深入kotlin】 - 方法引用和属性引用相关推荐

  1. 光脚丫学LINQ(039):字段引用还是属性引用

    视频演示:http://u.115.com/file/f28ae6dc98 重点介绍 所谓字段引用和属性引用,实际上是LINQ to SQL的映射关系中,实体类对象获取关联对象时所使用的类成员是字段, ...

  2. java8 方法引用详解_Java8中如何通过方法引用获取属性名详解

    前言 在我们开发过程中常常有一个需求,就是要知道实体类中Getter方法对应的属性名称(Field Name),例如实体类属性到数据库字段的映射,我们常常是硬编码指定 属性名,这种硬编码有两个缺点. ...

  3. kotlin - 扩展方法和扩展属性

    kotlin - 扩展方法和扩展属性 我们都知道java要扩展一个已有类的方法和属性必须采用继承.组合或直接修改现有类来进行功能和属性的扩展.而kotlin是完全支持扩展方法和扩展属性的,这样我们就可 ...

  4. 无法获取未定义或 null 引用的属性“text”_【CSS】是时候开始用 CSS 自定义属性了...

    自定义属性(有时候也被称作CSS变量或者级联变量)是由CSS作者定义的,它包含的值可以在整个文档中重复使用.由自定义属性标记设定值(比如:--main-color: black;),由var() 函数 ...

  5. python 类静态属性_如何从Python中的类中引用静态属性?

    您面临的问题是因为您不了解类声明的作用域是如何工作的.类声明在其自己的作用域内执行.执行完成后,将创建一个新的类对象,并将获得的范围作为其__dict__附加到该类.在 注意:类范围是从方法范围内搜索 ...

  6. python实例属性引用-python之对象(实例)

    1.对象是关于类而实际存在的一个例子,即实例 #类实例化得到g1这个实例 class Garen: camp="Demacia" def __init__(self,nicknam ...

  7. 【LabVIEW懒人系列教程-小白入门】1.20LabVIEW之引用与属性节点

    上期1.19作业讲解 按照题目要求,将两个簇控件进行解绑-运算-捆绑输出即可. 今天给大家讲解labview中控制控件属性的常用方法:引用与属性节点. 引用:在Labview中常称为引用句柄,在Win ...

  8. vue调用 webVideoCtrl.js 海康设备调试 无法获取未定义或 null 引用的属性“HWP_SubmitHttpRequest”

    海康网页端开发遇到 无法获取未定义或 null 引用的属性"HWP_SubmitHttpRequest" 经过代码调试后,发现原因: 没有指定视频展示对应的div 解决方法: // ...

  9. SCRIPT5007: 无法获取未定义或 null 引用的属性“call”

    问题 ie浏览器中打开vue项目时报错:SCRIPT5007: 无法获取未定义或 null 引用的属性"call" 解决方法 1.确保webpack版本在 2.6.1以上 2.安装 ...

最新文章

  1. J2EE基础之JSP
  2. AIDL中callback的实现
  3. python函数能否增强代码可读性_python——初识函数
  4. java面向对象(1)
  5. Nginx配置pathinfo
  6. ubuntu18.04安装VCS+verdi错误集锦
  7. java.lang.UnsatisfiedLinkError: com.jacob.com.D...
  8. Zigbee费尽心思做mesh网究竟在智能家居中有什么用?
  9. 项目经济规模的估算方法_估算英国退欧的经济影响
  10. GMF 教程 Mindmap 5
  11. 【Maven学习笔记(二)】Maven的安装与配置
  12. centos7无GUI情况安装Xvfb、selenium、chrome
  13. PRNet:人脸3D重建与密集对齐
  14. jstack 线程状态分析_面试官:说说你是怎么用JDK监控和故障处理工具的吧?例如jstack...
  15. Jmeter中的几个重要测试指标释义
  16. JS将Date加八小时
  17. spring cloud config动态刷新_Spring Cloud学习笔记--配置中心(Config)
  18. Tapestry 教程(四)探索项目结构
  19. opendrive map with UE4
  20. 高等代数期末考试题库及答案_高等代数Ⅱ答案期末答案

热门文章

  1. 帝国cms 调用指定栏目 名称 链接 别名
  2. java计算机毕业设计恒美服饰原材料采购预约配送系统MyBatis+系统+LW文档+源码+调试部署
  3. 屏蔽QQ群消息的简单方法(转)
  4. java数据结构红黑树上旋下旋_存储系统的基本数据结构之一: 跳表 (SkipList)
  5. centos7搭建主从DNS服务器
  6. tp5多文件异步上传+七牛云+预览图
  7. 国产化软硬件系统解决方案
  8. 几种常用的电平转换方案
  9. 体验 TiDB v6.0.0 之 Clinic
  10. android智能手机的发展历史,Android智能手机运存发展史