前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 反射 VS Kotlin 反射

Java 反射 VS Kotlin 反射

作者头像
fengzhizi715
发布2021-11-24 13:04:19
2.4K0
发布2021-11-24 13:04:19
举报

Kotlin 跟 Java 可以无缝衔接,因此 Kotlin 能够使用 Java 的反射机制。另外,Kotlin 也有自己的反射机制,需要额外地引入 kotlin-reflect.jar。

代码语言:javascript
复制
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

kotlin-reflect.jar 中包含 kotlin.reflect.full 和 kotlin.reflect.jvm。

  • kotlin.reflect.full 是主要的 Kotlin 反射 API
  • kotlin.reflect.jvm 用于 Kotlin 反射和 Java 反射的互操作。

Kotlin 反射的特性包含:

  • 提供对属性和可空类型的访问权限,这是由于 Java 没有属性和可空类型的概念。
  • Kotlin 反射不是 Java 反射的替代品,而是功能的增强。
  • 可以使用 Kotlin 反射来访各种基于 JVM 语言编写的代码。

下面以 Java 的反射和 Kotlin 的反射进行对比。

一. 类引用,获取 Class 对象

Java 获取 Class 对象的方式
  • Class.forName("完整的包名+类名")
代码语言:javascript
复制
Class<?> clazz = Class.forName("xxx.xxx.MyClass")
  • 类名.class
代码语言:javascript
复制
Class<?> clazz = MyClass.class;
  • 实例对象.getClass()
代码语言:javascript
复制
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
Kotlin 获取 Class 对象的方式
  • 调用的类是 Java 类,需要添加 .java 后缀( KClass 的扩展属性 java)
代码语言:javascript
复制
val clazz = MyClass::class.java
  • 添加 Java 实例对象的.javaClass后缀(Java 实例对象的扩展属性 javaClass)。
代码语言:javascript
复制
val obj = MyClass()
val clazz = obj.javaClass
  • 调用的类是 Kotlin 类
代码语言:javascript
复制
val clazz = MyClass::class

此时 clazz 的类型是 KClass 类型,KClass 的一个实例表示对 Kotlin 类的引用。KClass 也是 Kotlin 反射 API 的主要入口。

在 Kotlin 中,字节码对应的类也是 kotlin.reflect.KClass。

Kotlin 的引用类有两种方式:类名::class对象::class,它们获取的都是相同的 KClass 实例。

即处于同一个类加载器中,给定的类型只能返回一个 KClass 实例。即使多次尝试实例化 KClass, 仍然只能获取同一对象的引用, Kotlin 不会创建新的引用。

二. 构造函数引用,获取类的构造函数

Java 获取类的构造函数

Java 在获取 Class 实例之后,可以获取其中的构造函数。Java 获取类的构造函数对应的是 java.lang.reflect.Constructor,有以下五种方式:

代码语言:javascript
复制
// 获取参数列表是 parameterTypes,访问 public 的构造函数
public Constructor getConstructor(Class[] parameterTypes)

// 获取所有 public 构造函数
public Constructor[] getConstructors()

// 获取参数列表是 parameterTypes,并且是类自身声明的构造函数,访问控制符包含 public、protected 和 private 的函数。
public Constructor getDeclaredConstructor(Class[] parameterTypes)

// 获取类自身声明的全部的构造函数,包含 public、protected和private 的函数。
public Constructor[] getDeclaredConstructors()

// 如果类声明在其它类的构造函数中,返回该类所在的构造函数,如果存在则返回,不存在返回null
public Constructor getEnclosingConstructor()
Kotlin 获取类的构造函数

Kotlin 在获取 KClass 实例之后,可以获取它的全部构造函数。

代码语言:javascript
复制
// 类中声明的所有构造函数。
public val constructors: Collection<KFunction<T>>

Kotlin 通过::操作符并添加类名来引用构造函数。例如:

代码语言:javascript
复制
class Foo()

fun function(action: () -> Foo) {
    val foo: Foo = action.invoke()
    println(foo) // Foo@66d3c617
}

fun main() {
    function(::Foo)
}

对于含有参数的构造函数,也一样适用:

代码语言:javascript
复制
class Foo(val x:String)

fun main() {
    val foo = ::Foo // 表示引用 Foo 类的构造函数
    val f = foo("test")
    println(f)
}

三. 函数引用,获取类的成员函数

Java 获取类的成员函数

Java 获取类的成员函数对应的是 java.lang.reflect.Method,有以下五种方式:

代码语言:javascript
复制
// 根据函数名 name,参数 parameterTypes 获取类自身的 public 的函数(包括从基类继承的、从接口实现的所有 public 函数)
public Method getMethod(String name, Class[] parameterTypes)

// 获取全部 public 的函数(包括从基类继承的、从接口实现的所有 public 函数)
public Method[] getMethods()

// 根据函数名 name,参数 parameterTypes,获取类自身声明的函数,包含 public、protected 和 private 方法。
public Method getDeclaredMethod(String name, Class[] parameterTypes)

// 获取类自身声明的函数,包含 public、protected 和 private 方法。
public Method[] getDeclaredMethods()

// 如果此 Class 对象表示某一方法中的一个本地或匿名类,则返回 Method 对象,它表示底层类的立即封闭方法。若不存在,返回 null。
public Method getEnclosingMethod()
Kotlin 获取类的成员函数

Kotlin 通过反射调用函数,需要 KFunction 实例。KFunction 实例可以通过两种方式获得:一种是方法引用,另一种是通过 KClass 提供的 API 获得 KFunction 实例。

方法引用是简化版本的 Lambda 表达式,它和 Lambda 表达式拥有相同的特性。

Kotlin 和 Java 的方法引用使用::操作符,Kotlin 除了可以引用类中成员函数、扩展函数还可以引用顶层(top-level)函数。

方法引用属于 KFunction 的子类型 KFunctionN,N 代表具体的参数数量,例如 KFunction2<T1, T2, R>。KFunction 与 Lambda 的 Function 类似,都可以通过invoke()方法调用该引用函数。

例如:

代码语言:javascript
复制
val sum = {
        x: Int, y: Int -> x + y
}

fun sumFunction(x: Int, y: Int) = x + y

fun main() {
    println(sum(3,5))

    val sunFunc:KFunction2<Int,Int,Int> = ::sumFunction
    println(sunFunc.invoke(3,5))

    println(sunFunc.call(3, 5))
}

执行结果:

代码语言:javascript
复制
8
8
8

KFunctionN 类型属于合成的编译器生成类型,我们无法在包 kotlin.reflect 中找到它们的声明。

在上述例子中,最后一行代码:

代码语言:javascript
复制
println(sunFunc.call(3, 5))

其实这里调用 KCallable 接口的call()方法。

代码语言:javascript
复制
public actual interface KCallable<out R> : KAnnotatedElement {

    ......

    /**
     * Calls this callable with the specified list of arguments and returns the result.
     * Throws an exception if the number of specified arguments is not equal to the size of [parameters],
     * or if their types do not match the types of the parameters.
     */
    public fun call(vararg args: Any?): R

    ......
}

对于 KFunction 对象,也可以使用 KCallable 的call()方法来调用被引用的函数。call()方法使用指定的参数列表,开发者需要自行匹配所使用的实参类型和数量,如果其类型与参数的类型不匹配,则会引发异常。

而 KFunctionN 的invoke()方法的形参类型和返回值类型是可以确定的,调用它的 invoke() 时编译器会帮我们做检查。

当然,也可以使用 KFunctionN 来引用类的扩展函数。引用扩展函数的用法跟引用成员函数的用法是一致的。

代码语言:javascript
复制
data class User(var name:String,var password:String)

fun User.validatePassword():Boolean = this.password.length > 6

val validate:KFunction1<User,Boolean> = User::validatePassword

fun main() {

    val user = User("tony","12345")
    println(user.validatePassword())
    println(validate.invoke(user))
    println(validate.call(user))
}

四. 属性引用,获取类的成员变量

Java 获取类的成员变量

Java 获取类的成员变量对应的是 java.lang.reflect.Field,有以下四种方式:

代码语言:javascript
复制
// 获取相应的类自身声明的 public 成员变量(包括从基类继承的、从接口实现的)
public Field getField(String name)

// 获取类自身声明全部的 public 成员变量(包括从基类继承的、从接口实现的)
public Field[] getFields()

// 获取相应的类自身声明的成员变量,包含 public、protected 和 private 成员变量。
public Field getDeclaredField(String name)

// 获取类自身声明的成员变量,包含 public、protected 和 private 。
public Field[] getDeclaredFields()
Kotlin 获取类的属性

Kotlin 没有成员变量的概念,只有属性的概念。下面展示了引用不同属性的方式。

不可变属性的引用

Kotlin 使用::属性来获取不可变属性的引用,并返回 KProperty<V> 类型的值。它的get()方法会返回属性的值,它的name属性会返回可变属性的名称。

代码语言:javascript
复制
val x = 1
val y = "hello"

fun main() {
    println(::x)
    println(::x.get())
    println(::x.name)
    println(::y)
    println(::y.get())
    println(::y.name)
}

执行结果:

代码语言:javascript
复制
val x: kotlin.Int
1
x
val y: kotlin.String
hello
y
可变属性的引用

Kotlin 使用::属性来获取可变属性的引用,并返回 KMutableProperty<V> 类型的值。它除了上述的get()方法、name属性,还支持set()方法修改属性的值。

代码语言:javascript
复制
var x = 1
var y = "hello"

fun main() {
    println(::x)
    ::x.set(0)
    println(::x.get())
    println(::x.name)
    println(::y)
    ::y.set("world")
    println(::y.get())
    println(::y.name)
}

执行结果:

代码语言:javascript
复制
var x: kotlin.Int
0
x
var y: kotlin.String
world
y
扩展属性的引用

Kotlin 使用类名::属性来获取扩展属性的引用,并返回 KProperty1<T, out V> 类型的值。它的get()getValue()方法会返回扩展属性的值。

代码语言:javascript
复制
class Extension

val Extension.x: Int
    get() = 1

fun main() {
    println(Extension::x) // Extension.x: kotlin.Int
    val extension = Extension()
    println(Extension::x.get(extension)) // 1
    println(Extension::x.getValue(extension,Extension::x)) // 1
}
类的成员属性的引用

Kotlin 使用类名::属性来获取成员属性的引用,并返回 KProperty1<T, out V> 类型的值。它的get()call()方法会返回成员属性的值。

代码语言:javascript
复制
class Bar1(val x: Int)
class Bar2(val x: Int,val y:String)

fun main() {
    val bar1 = Bar1(1)
    val prop = Bar1::x
    println(prop.call(bar1))
    println(prop.getter.call(bar1))
    println(prop.get(bar1))

    val bar2 = Bar2(2,"test")
    val propX = Bar2::x
    val propY = Bar2::y
    println(propX.call(bar2))
    println(propY.call(bar2))
    println(propX.get(bar2))
}

执行结果:

代码语言:javascript
复制
1
1
1
2
test
2

另外,这里的call()方法,依然是 KCallable 接口的call()方法。call()方法的调用最终会调用该属性的 getter 属性。

毕竟,KProperty、KFunction 的超类型都是 KCallable

五. 获取类的其它信息

Java 获取类的其它信息

Java 获取类的注解信息,对应的是 java.lang.annotation.Annotation 接口,有以下三种方式:

代码语言:javascript
复制
// 获取类的 annotationClass 类型的注解 (包括从基类继承的、从接口实现的所有 public 成员变量)
public Annotation<A> getAnnotation(Class annotationClass)

// 获取类的全部注解 (包括从基类继承的、从接口实现的所有 public 成员变量)
public Annotation[] getAnnotations()

// 获取类自身声明的全部注解 (包含 public、protected 和 private 成员变量)
public Annotation[] getDeclaredAnnotations()

Java 获取类的接口和基类的信息,对应的是 java.lang.reflect.Type 接口,有以下两种方式:

代码语言:javascript
复制
// 获取类实现的全部接口
public Type[] getGenericInterfaces()

// 获取类的直接超类的 Type
public Type getGenericSuperclass()

Java 获取类的其它描述信息,包括:

代码语言:javascript
复制
// 获取类名
public String getSimpleName()

// 获取完整类名
public String getName()

// 判断类是不是枚举类
public boolean isEnum()

// 判断obj是不是类的实例对象
public boolean isInstance(Object obj)

// 判断类是不是接口
public boolean isInterface()

// 判断类是不是局部类。局部类所属范围:在块、构造器以及方法内,这里的块包括普通块和静态块。
public boolean isLocalClass()

// 判断类是不是成员类
public boolean isMemberClass()

// 判断类是不是基本类型。
public boolean isPrimitive()
Kotlin 获取类的其它信息

Kotlin 能够获取更多的类的信息,包括:

代码语言:javascript
复制
// 获取类的名字
public val simpleName: String?

 // 获取类的全包名
public val qualifiedName: String?

// 如果这个类声明为 object,则返回其实例,否则返回 null
public val objectInstance: T?

// 获取类的可见性
@SinceKotlin("1.1")
public val visibility: KVisibility?

// 判断类是否为 final 类,Kotlin 默认类是 final 类型的,除非这个类声明为 open 或者 abstract
@SinceKotlin("1.1")
public val isFinal: Boolean

// 判断类是否是open,(abstract  类也是 open),表示这个类可以被继承
@SinceKotlin("1.1")
public val isOpen: Boolean

// 判断类是否为抽象类
@SinceKotlin("1.1")
public val isAbstract: Boolean

// 判断类是否为密封类
@SinceKotlin("1.1")
public val isSealed: Boolean

// 判断类是否为 data class
@SinceKotlin("1.1")
public val isData: Boolean

// 判断类是否为成员类
@SinceKotlin("1.1")
public val isInner: Boolean

// 判断类是否为 companion object
@SinceKotlin("1.1")
public val isCompanion: Boolean

// 判断类是否为 functional interface
@SinceKotlin("1.4")
public val isFun: Boolean

// 获取类中定义的其他类,包括内部类和嵌套类
public val nestedClasses: Collection<KClass<*>>

// 判断一个对象是否为此类的实例
@SinceKotlin("1.1")
public fun isInstance(value: Any?): Boolean

// 获取这个类的泛型列表
@SinceKotlin("1.1")
public val typeParameters: List<KTypeParameter>

// 获取类其直接基类的列表
@SinceKotlin("1.1")
public val supertypes: List<KType>

// 如果该类是密封类,返回子类的列表,否则返回空列表。
@SinceKotlin("1.3")
public val sealedSubclasses: List<KClass<out T>>

// 获取类所有的基类
val KClass<*>.allSuperclasses: Collection<KClass<*>>

// 获取类的伴生对象 companionObject
val KClass<*>.companionObject: KClass<*>?

六. Java 反射与 Kotlin 反射的互操作性

为一个 Kotlin 属性获取一个 Java 的 getter/setter 方法或者幕后字段,需要使用 kotlin.reflect.jvm 包。

幕后字段 (backing field) 是 Kotlin 属性自动生成的字段,它只能在当前属性的访问器(getter、setter)内部使用。

  • KProperty 的扩展属性 javaGetter:返回给定属性的 getter 相对应的 Java 方法实例,如果该属性没有 getter,则返回 null。
代码语言:javascript
复制
val KProperty<*>.javaGetter: Method?
    get() = getter.javaMethod
  • KProperty 的扩展属性 javaField:返回给定属性的幕后字段相对应的 Java 字段实例,如果属性没有幕后字段,则返回 null。
代码语言:javascript
复制
val KProperty<*>.javaField: Field?
    get() = this.asKPropertyImpl()?.javaField
  • KMutableProperty 的扩展属性 javaSetter:返回给定可变属性的 setter 相对应的 Java 方法实例,如果该属性没有 setter,则返回 null。
代码语言:javascript
复制
val KMutableProperty<*>.javaSetter: Method?
    get() = setter.javaMethod

如果要获取对应于 Java 的 Kotlin 类,使用 .kotlin 扩展属性返回 KClass 实例。

代码语言:javascript
复制
public val <T : Any> Class<T>.kotlin: KClass<T>
    @JvmName("getKotlinClass")
    get() = Reflection.getOrCreateKotlinClass(this) as KClass<T>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/9/27 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 类引用,获取 Class 对象
    • Java 获取 Class 对象的方式
      • Kotlin 获取 Class 对象的方式
      • 二. 构造函数引用,获取类的构造函数
        • Java 获取类的构造函数
          • Kotlin 获取类的构造函数
          • 三. 函数引用,获取类的成员函数
            • Java 获取类的成员函数
              • Kotlin 获取类的成员函数
              • 四. 属性引用,获取类的成员变量
                • Java 获取类的成员变量
                  • Kotlin 获取类的属性
                    • 不可变属性的引用
                    • 可变属性的引用
                    • 扩展属性的引用
                    • 类的成员属性的引用
                • 五. 获取类的其它信息
                  • Java 获取类的其它信息
                    • Kotlin 获取类的其它信息
                    • 六. Java 反射与 Kotlin 反射的互操作性
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档