kavaref-extension

Maven CentralMaven metadata URL

这是 KavaRef 相关功能的扩展依赖。

配置依赖

你可以使用如下方式将此模块添加到你的项目中。

SweetDependency (推荐)

在你的项目 SweetDependency 配置文件中添加依赖。

libraries:
  com.highcapable.kavaref:
    kavaref-extension:
      version: +

在你的项目 build.gradle.kts 中配置依赖。

implementation(com.highcapable.kavaref.kavaref.extension)

Version Catalog

在你的项目 gradle/libs.versions.toml 中添加依赖。

[versions]
kavaref-extension = "<version>"

[libraries]
kavaref-extension = { module = "com.highcapable.kavaref:kavaref-extension", version.ref = "kavaref-extension" }

在你的项目 build.gradle.kts 中配置依赖。

implementation(libs.kavaref.extension)

请将 <version> 修改为此文档顶部显示的版本。

传统方式

在你的项目 build.gradle.kts 中配置依赖。

implementation("com.highcapable.kavaref:kavaref-extension:<version>")

请将 <version> 修改为此文档顶部显示的版本。

功能介绍

你可以 点击这里在新窗口中打开 查看 KDoc。

Class 扩展

KavaRef 提供了一些扩展,在处理 Class 对象时会更加方便。

KavaRef 对 Class 的扩展同样添加了 KClass 扩展,作用是调用 KClass.java,写法上比直接使用 Some::class.java 更加简洁。

创建 Class 对象

例如我们需要使用字符串类名创建一个 Class 对象。

示例如下

val myClass = "com.example.MyClass".toClass()
// 你可以使用带有 OrNull 后缀的方法在找不到 Class 时返回 null 而不是抛出异常
val myClassOrNull = "com.example.MyClass".toClassOrNull()

这些方法统一使用 ClassLoaderProvider 来获取默认的 ClassLoader,你可以设置默认的 ClassLoader 以影响全局功能。

示例如下

ClassLoaderProvider.classLoader = MyCustomClassLoader()

你也可以手动向 toClass 方法传入一个 ClassLoader 参数来指定使用哪个 ClassLoader

Class 对象引用

在 Kotlin 中引用 Java Class 需要写很长的声明,例如 MyClass::class.java,此时你可以用以下方式来简化。

示例如下

val myClass = classOf<MyClass>()

你可以使用 isSubclassOf 方法来判断一个 Class 是否是另一个 Class 的子类。

示例如下

val isSubclass = MyClass::class isSubclassOf MySuperClass::class
// 当然,它也有一个对应的反义判断方法
val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class

你还可以使用 hasSuperclasshasInterfaces 方法来判断一个 Class 是否有超类或接口。

特别注意

classOf 方法传入的 Class 默认会进行 Java 包装类的拆箱操作,无论你传入的是类似 kotlin.Boolean 还是 java.lang.Boolean (参考下方的 Java 包装类扩展), 如果你需要避免传入的 Class 被拆箱变为原始类型,你需要明确设置 primitiveType = false 参数。

创建新的实例

KavaRef 为 Class 提供了一个方法来方便地创建一个新的实例,你不需要考虑构造参数的类型,你只需要传入对应的参数即可立即创建一个新的实例。

示例如下

val myClass = MyClass::class.createInstance("Hello", 123)
// 你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常
val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123)
// createInstance 方法默认仅过滤公开的构造方法,如果你需要调用非公有构造方法,请设置 isPublic = false
val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false)
// 如果你想指定创建实例的类型使用另一个类型,可以使用以下方法
val mySuperClass = MyClas::class.createInstanceAsType<MySuperClass>("Hello", 123)
// 同样地,你也可以使用带有 OrNull 后缀的方法在创建失败时返回 null 而不是抛出异常
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("Hello", 123)

小提示

createInstance 方法在成功匹配一次后,会将结果进行缓存防止重复反射造成的性能损耗,它是线程安全的,你可以放心在任何标准场景下使用。

特别注意

当你传入带有 null 的参数时,KavaRef 会尝试将其作为可匹配到条件的一部分 (模糊条件),准确性可能会下降。

createInstance 方法不允许所有参数均为 null 的情况 (条件完全模糊),会直接抛出异常,因为这种情况无法确定要创建哪个实例。

Class 修饰符

KavaRef 也对 Modifier 进行了扩展,你可以直接使用 Class.isPublic 等方法来判断一个 Class 的修饰符。

VariousClass

KavaRef 提供了 VariousClass 类来装载不确定完整类名的 Class 对象,并返回成功匹配到的第一个。

此功能通常可用于 Android 应用中那些被 R8 混淆后的类名。

示例如下

// 假设在 A 版本中,这个类为 com.example.a,
// 在 B 版本中,这个类为 com.example.b
val myClass = VariousClass("com.example.a", "com.example.b").load()
// 你也可以使用后缀名为 OrNull 的方法在找不到 Class 时返回 null 而不是抛出异常
val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull()

延迟装载 Class 对象

KavaRef 提供了 LazyClass 类来延迟装载 Class 对象。

你可以在需要时再装载 Class,而不是在创建时就立即装载,这可以解决一些需要运行时或运行到特定条件下才需要装载的 Class

示例如下

// 定义一个不可为空延迟装载的 Class 并托管给 myClass
val myClass by lazyClass("com.example.MyClass")
// 定义一个可为空延迟装载的 Class 并托管给 myClassOrNull
val myClassOrNull by lazyClassOrNull("com.example.MyClass")
// 它亦可支持传入 VariousClass
val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b"))
// 在需要时调用即装载
myClass.resolve()
myClassOrNull?.resolve()
otherClassOrNull?.resolve()

ClassLoader 扩展

KavaRef 还为 ClassLoader 提供了一些实用的扩展方法。

示例如下

// 假设这就是你的 ClassLoader
val classLoader: ClassLoader
// 装载一个 Class,在装载失败时返回 null
val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass")
// 判断这个 Class 是否存在于当前 ClassLoader 中
val isClassExists = classLoader.hasClass("com.example.MyClass")

数组 Class 扩展

在 Java 中,数组的 Class 对象是一个特殊的 Class 对象,通常,我们创建它的方式如下。

例如创建一个 java.lang.String[]Class 对象。

示例如下

val arrayClass = java.lang.reflect.Array.newInstance(String::class.java, 0).javaClass

这样写起来非常长,而且不方便维护,所以 KavaRef 提供了一个方法来简化这个过程。

现在,创建 java.lang.String[]Class 对象可以这样写。

示例如下

val arrayClass = ArrayClass(String::class)

Member 扩展

KavaRef 提供了一些扩展方法来简化对 Member 的操作。

你可以在任何 Member 对象上使用 makeAccessible 方法来设置其可访问性。

如果 MemberAccessibleObject 类型即可生效。

示例如下

// 假设这个是你当前的 Member 对象
val method: Method
// 设置方法可访问
method.makeAccessible()

同样地,KavaRef 也对 Modifier 进行了扩展,你可以直接使用 Member.isPublic 等方法来判断一个 Member 的修饰符。

Type 扩展

在 Java 中操作类型或泛型类型时,通常需要使用 Type 接口及其子接口来处理。

KavaRef 提供了一些扩展方法来简化对 Type 的操作。

例如,你可以将一个符合要求的 Type 转换为 Class 对象。

示例如下

val type: Type
val clazz = type.toClass()
// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常
val clazzOrNull = type.toClassOrNull()

你也可以将符合要求的 Type 转换为 ParameterizedType 对象。

示例如下

val type: Type
val parameterizedType = type.asParameterizedType()
// 你也可以使用后缀名为 OrNull 的方法在转换失败时返回 null 而不是抛出异常
val parameterizedTypeOrNull = type.asParameterizedTypeOrNull()

你还可以使用以下方式获取超类中的泛型参数数组,这在一些超类与子类的封装操作中会经常用到。

示例如下

val myClass: Class<*>
// 获取 myClass 的超类的泛型参数数组,获取失败或无法获取时将返回空数组
val arguments = myClass.genericSuperclassTypeArguments()

Java 包装类扩展

在 Kotlin 中直接使用 Boolean::classByte::class 等方式获取到的是 Java 的原始类型 booleanbyte 而不是它们的包装类。

如果你需要获取 Java 的包装类,你需要使用完整的 java.lang.Boolean::classjava.lang.Byte::class 等方式或使用 Boolean::class.javaObjectTypeByte::class.javaObjectType

所以,KavaRef 提供了一些类型别名来处理 Java 的包装类,现在你只需要在这些类型加上 J 前缀即可,例如 JBoolean::class, 它等价于 java.lang.Boolean::class,部分类型需要填写全称,例如 JInteger::class