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