Kotlin-反射

来源:互联网 发布:java怎么引入包 编辑:程序博客网 时间:2024/05/19 09:13

反射是一个语言集,并且库的特色是允许我们在程序运行时反思程序的结构。在Kotlin中,函数和属性是一级成员,可以通过简单的函数或灵活的样式来反思它们。
==在Java平台中,如果运行时组件想用反射功能可以通过添加单独的jar文件,这个可以避免增加不需要反射功能的应用程序的大小,如果你需要使用反射,只要确保这个jar文件有添加到工程的classpath中.==

类引用

反射应用的最基本特点就是在运行时获取一个引用到类中,为了要获得这个静态类的引用,可以使用以下类语法:

val c=MyClass::class

这个引用的类型是KClass.

==注意:Kotlin中类引用和Java中类引用是不同的,要获得java类的引用,需要用到KClass实例的’.java’属性==

绑定类引用(版本1.1以后)

你可以通过和’::class’类似的语法把这个对象当做接收器来获取指定对象的引用:

val widget: Widget = ...assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }

你得到一个确切的参考类的对象引用,例如goodwidget或badwidget,尽管接收表达式的类型(是Widget)

函数引用

当我们有一个命名函数声明如下:

fun isOdd(x: Int) = x % 2 != 0

我们可以简单直接的使用它(isOdd(5)),并且我们还可以把它当做一个参数来使用,例如在另外一个函数中,为了达到这个目的,我们可以使用’::’操作符:

val numbers = listOf(1, 2, 3)println(numbers.filter(::isOdd)) // 打印 [1, 3]

这里的’::isOdd’就是一个函数类型((Int)>Boolean)的值

当预期的类型可以通过上下文知道的时候,我们可以把’::’当做重载函数来使用:

fun isOdd(x: Int) = x % 2 != 0fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"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’的拓展函数.

示例:函数组合

请思考下面这个函数:

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

通过它可以返回两个函数的组合:’compose(f,g)=f(g(*))’.现在你可以把它应用在实际引用中:

fun length(s: String) = s.lengthval oddLength = compose(::isOdd, ::length)val strings = listOf("a", "ab", "abc")println(strings.filter(oddLength)) // 打印了 "[a, abc]"

属性引用

在Kotlin中,访问属性是属于第一级对象,我们可以使用’::’操作符:

var x = 1fun main(args: Array<String>) {    println(::x.get()) // prints "1"    ::x.set(2)    println(x)         // prints "2"}

表达式’::x’等价于类型为KProperty的一个属性,它可以允许我们通过get获取值,也可以通过name属性来查找属性名,更多详细请查看KProperty相关章节

对于可变属性,例如:var y = 1,::y则返回类型为KMutableProperty的值,并且还有set方法.

属性引用可以应用在没有参数的函数中:

val strs = listOf("a", "bc", "def")println(strs.map(String::length)) // prints [1, 2, 3]

如果是访问类中的这个属性,那么我们可以这样用:

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

对于拓展属性:

val String.lastChar: Char    get() = this[length - 1]fun main(args: Array<String>) {    println(String::lastChar.get("abc")) // prints "c"}

跟Java反射进行交互

在Java平台上,标准库中包含有反射类的拓展部分,它有提供跟Java反射对象互转的功能(package kotlin.reflect.jvm包下),举个例子,要找出隐性属性或Kotlin属性中Java方法,你可以这样做:

import kotlin.reflect.jvm.*class A(val p: Int)fun main(args: Array<String>) {    println(A::p.javaGetter) // prints "public final int A.getP()"    println(A::p.javaField)  // prints "private final int A.p"}

如果要从java类中获取相对应的Kotlin类,可以用’.Kotlin’拓展属性

fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin

构造方法引用

构造的引用也和方法和属性类似,它们可以用于任何一个函数类型的对象,该函数的对象与构造函数的参数相同,并返回该类型的对象,构造方法的引用也可以使用’::’操作符和类名的方式。我们可以思考下面这个函数,改函数无参并返回Foo类型:

class Foofun function(factory: () -> Foo) {    val x: Foo = factory()}

在Foo类中的无参构造方法中,我们可以使用’::Foo’ 来代替,现在我们可以这样简便的调用:

function(::Foo)

绑定函数和属性引用(版本1.1以后)

你可以引用特定对象的实例方法

val numberRegex = "\\d+".toRegex()println(numberRegex.matches("29")) // prints "true"val isNumber = numberRegex::matchesprintln(isNumber("29")) // prints "true"

我们使用保存’matches’的引用来代替直接使用,这个引用绑定到它的接收器上,这个引用可以直接使用(像上面那个例子一样)或在需要这个函数表达式的时候再使用:

val strings = listOf("abc", "124", "a70")println(strings.filter(numberRegex::matches)) // prints "[124]"

通过绑定的类型和对应没绑定的引用进行对比,被绑定的可被调用的引用有它自己的接收器可以接触它,因此这个接收器的类型不单单是一个参数:

val isNumber: (CharSequence) -> Boolean = numberRegex::matchesval matches: (Regex, CharSequence) -> Boolean = Regex::matches

属性引用也可以同样被绑定:

val prop = "abc"::lengthprintln(prop.get())   // prints "3"
原创粉丝点击