Kotlin-35.反射(Reflection)

官方文档: http://kotlinlang.org/docs/reference/reflection.html

1.反射概念(Reflection)

反射是一套语言功能库,允许在程序运行时自省/内省(introspect)程序结构!
与java语言不同, 函数和属性是Kotlin世界的一等公民,
对它们自省与函数式或响应式风格密切相关(functional/reactive style)!

自省/内省(introspect): 在运行时获取类,属性,函数的名称和类型!

提示:
    在Java平台(JVM)上使用kotlin反射功能,需要在项目中添加kotlin反射库的jar包(kotlin-reflect.jar)!        
    因为kotlin反射库JAR文件(kotlin-reflect.jar)是单独分发的,不包含在kotlin标准库中,
    这是为了减少[不使用反射功能的应用]所需运行库的大小!

2.类引用(Class References)

kotlin最基本的反射功能是获取Kotlin类的运行时引用(runtime reference)!
获取静态已知的Kotlin类引用,可用[类字面值语法](class literal syntax):
    val c = MyClass::class

kotlin类引用是KClass类型的一个值!       
Java类引用和Kotlin不同,获取Java类引用要在KClass实例对象上使用.java属性:
    fun main(args: Array<String>) {
        println(String::class)      //输出class kotlin.String
        println(String::class.java) //输出class java.lang.String
    }

自kotlin 1.1起,开始有[绑定类引用](Bound Class References),
使用对象作为接收者(::class语法)获取指定对象的类引用:
    val widget: Widget = ...
    assert(widget is GoodWidget) {
    //获取widget对象的精确类(实际类)的引用, GoodWidget 或 BadWidget
        "Bad widget: ${widget::class.qualifiedName}"
    }

3.函数引用(Function References)

有一个命名函数声明如下:
    fun isOdd(x: Int) = x % 2 != 0

很容易直接调用它isOdd(5),但也可以把它作为一个值传递,例如传给另一个函数,
为此,可使用 ::操作符传递一个函数:
    fun main(args: Array<String>) {
        val numbers = listOf(1, 2, 3)
        println(numbers.filter(::isOdd)) // 输出 [1, 3]
    }
    //filter函数的参数类型是 (Int) -> Boolean,
    //而isOdd函数类型也是(Int) -> Boolean, 所以程序正常运行!

当从上下文可知函数类型时,::可用于重载函数,例如:
    fun isOdd(x: Int) = x % 2 != 0
    fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"

    fun main(args: Array<String>) {
        val numbers = listOf(1, 2, 3)
        println(numbers.filter(::isOdd)) //引用isOdd(x: Int)

        //把方法引用存储在已知类型的变量中
        val predicate: (String) -> Boolean = ::isOdd //引用isOdd(x: String)
    }

使用类的成员函数或扩展函数时需要限定,例如:
    String::toCharArray 为String类型提供了一个扩展函数: String.() -> CharArray

组合函数(Function Composition)的示例如下:
    fun isOdd(x: Int) = x % 2 != 0
    fun length(s: String) = s.length

    fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
        return { x -> f(g(x)) }
    }

    //本例功能是输出在数组/集合中长度为奇数的元素
    fun main(args: Array<String>) {
        val strings = listOf("a", "ab", "abc")
        
        //oddLength是组合函数/lambda表达式,就是x -> isOdd(length(x)) 
        val oddLength = compose(::isOdd, ::length)
        println(strings.filter(oddLength)) // 输出 "[a, abc]"
        
        //等价于
        println(strings.filter{
            x -> isOdd(length(x)) // 输出 "[a, abc]"
        })
    }

4.属性引用(Property References)

在Kotlin中作为一级对象(first-class object)去访问属性,也可用::prop操作符:
    var x = 1
    fun main(args: Array<String>) {
        println(::x.get()) // 输出 "1"
        ::x.set(2)
        println(x)         // 输出 "2"
        println(::x.name)  // 输出 "x"
    }
    [表达式::x]的类型是KProperty<Int>,允许使用get()/set()读写值,或者使用name来获取属性名!
    对于可变属性var y = 1, ::y的类型是KMutableProperty<Int>,允许使用set()方法!

属性引用可用在不需要参数的函数:
    fun main(args: Array<String>) {
        val strs = listOf("a", "bc", "def")
        println(strs.map(String::length)) // 输出 [1, 2, 3]
    }

访问类成员的属性,可用class::prop.get/set限定它:
    class A(val p: Int)

    fun main(args: Array<String>) {
        val prop = A::p
        println(prop.get(A(1))) // 输出 "1"
        println(A::p.get(A(1))) // 输出 "1" 
    }

访问类的扩展属性,也可用class::prop.get/set限定它:
    val String.lastChar: Char
        get() = this[length - 1]

    fun main(args: Array<String>) {
        println(String::lastChar.get("abc")) // 输出 "c"
    }

5.与Java反射互操作(Interoperability Java Reflection)

在Java平台上,标准库包含反射类的扩展,提供了与Java反射对象之间映射(参见kotlin.reflect.jvm包)
1.获取与Kotlin属性对应的Java字段和get方法:
    import kotlin.reflect.jvm.*
    class A(val p: Int)

    fun main(args: Array<String>) {
        println(A::p.javaGetter) // 输出 "public final int A.getP()"
        println(A::p.javaField)  // 输出 "private final int A.p"
    }
2.获取与Java类对应的Kotlin类,可用.kotlin扩展属性:
    fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin

6.构造函数引用(Constructor References)

构造函数也可以像方法和属性一样引用,该引用与构造函数接受相同参数并且返回相应类型, 
使用::操作符+类名来引用构造函数:   
    class Foo
    
    //factory: () -> Foo 代表Foo类的构造函数
    fun function(factory: () -> Foo) {
        val x: Foo = factory()
    }

    //::Foo 代表类Foo的构造函数引用
    function(::Foo)

7.绑定函数与属性引用(Bound Function and Property Reference)

自kotlin 1.1起,开始有[绑定函数与属性引用](Bound Function and Property Reference)
1.绑定函数引用:
    //传统用法
    val numberRegex = "\\d+".toRegex()
    println(numberRegex.matches("29")) // 输出“true”   

    //绑定函数引用,isNumber存储/绑定函数引用
    val isNumber = numberRegex::matches
    println(isNumber("29")) // 输出“true”

    //直接调用函数引用
    val strings = listOf("abc", "124", "a70")
    println(strings.filter(numberRegex::matches)) // 输出“[124]”

    //引用的接收者的类型不再是参数
    val isNumber: (CharSequence) -> Boolean = numberRegex::matches
    val matches: (Regex, CharSequence) -> Boolean = Regex::matches

2.绑定属性引用:
    val prop = "abc"::length
    println(prop.get())   // 输出“3”

简书:http://www.jianshu.com/p/52ab689ce1ec
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/74852344
GitHub博客:http://lioil.win/2017/07/08/Kotlin-reflection.html
Coding博客:http://c.lioil.win/2017/07/08/Kotlin-reflection.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容