Skip to content

kavaref-extension ​

Maven CentralMaven metadata URL

This is an extended dependency for KavaRef-related features.

Configure Dependency ​

You can add this module to your project using the following method.

We recommend that you first refer to kavaref-bom to use BOM for unified version management.

Add dependency in your project's gradle/libs.versions.toml.

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

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

Configure dependency in your project's build.gradle.kts.

kotlin
implementation(libs.kavaref.extension)

Please change <version> to the version displayed at the top of this document.

Traditional Method ​

Configure dependency in your project's build.gradle.kts.

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

Please change <version> to the version displayed at the top of this document.

Function Introduction ​

You can view the KDoc click here.

Class Extension ​

KavaRef provides convenient extensions when dealing with Class objects.

KavaRef also brings these extensions to KClass, allowing you to call KClass.java more concisely than using Some::class.java directly.

Create Class Object ​

For example, we need to create a Class object using the string class name.

The following example

kotlin
val myClass = "com.example.MyClass".toClass()
// You can use a method with OrNull suffix to return null
// when the Class is not found instead of throwing an exception.
val myClassOrNull = "com.example.MyClass".toClassOrNull()

These methods use ClassLoaderProvider to get the default ClassLoader, you can set the default ClassLoader to affect global functionality.

The following example

kotlin
ClassLoaderProvider.classLoader = MyCustomClassLoader()

You can also manually pass a ClassLoader parameter to the toClass method to specify which ClassLoader to use.

If no ClassLoader is passed in and ClassLoaderProvider.classLoader is not set, the ClassLoader that loaded KavaRef will be used.

Tips

The toClass method does not initialize the Class by default (its static initializers will not be executed), if you need to initialize it, please set initialize = true.

Class Object Reference ​

Referring to a Java Class in Kotlin requires writing a very long statement, such as MyClass::class.java, which you can simplify in the following way.

The following example

kotlin
val myClass = classOf<MyClass>()

You can use the isSubclassOf method to determine whether a Class is another Class subclass.

The following example

kotlin
val isSubclass = MyClass::class isSubclassOf MySuperClass::class
// Of course, it also has a corresponding antonym of judgment.
val isNotSubclass = MyClass::class isNotSubclassOf MySuperClass::class

You can also use the hasSuperclass and hasInterfaces methods to determine whether a Class has a superclass or an interface.

Pay Attention

The Class passed in by the classOf method will perform unboxing of Java wrapper classes by default, whether you pass in something like kotlin.Boolean or java.lang.Boolean (see Java Wrapper Classes Extension below), If you need to avoid the incoming Class being unboxed into primitive types, you need to explicitly set the primitiveType = false parameter.

Create New Instance ​

KavaRef provides a way for Class to easily create a new instance. You don't need to consider the type of constructing parameters, you just need to pass in the corresponding parameters to create a new instance immediately.

The following example

kotlin
val myClass = MyClass::class.createInstance("Hello", 123)
// You can also use a method with the OrNull suffix to return null
// when creation fails instead of throwing an exception.
val myClassOrNull = MyClass::class.createInstanceOrNull("Hello", 123)
// The createInstance method only filters public constructors by default.
// If you need to call non-public constructors, please set isPublic = false.
val myClassWithPrivateConstructor = MyClass::class.createInstance("Private!", isPublic = false)
// If you want to specify the type to create an instance to use another type,
// you can use the following method.
val mySuperClass = MyClass::class.createInstanceAsType<MySuperClass>("Hello", 123)
// Similarly, you can use a method with the OrNull suffix to return null when
// creation fails instead of throwing an exception.
val mySuperClassOrNull = MyClass::class.createInstanceAsTypeOrNull<MySuperClass>("Hello", 123)

If more than one constructor can accept the parameters, KavaRef refers to Java's overload rules, constructors that match without unboxing are preferred, and then the one with the most specific parameter types is used.

The following example

kotlin
// Assume that MyClass has both MyClass(Object) and MyClass(String) constructors.
// MyClass(String) will be used here.
val myClass = MyClass::class.createInstance("Hello")

Tips

After the createInstance method is successfully matched once, it will cache the results to prevent performance losses caused by duplicated reflections. It is thread-safe and you can use it in any standard scenario with confidence.

The cache does not prevent the Class and its ClassLoader from being collected, so you can also use it in scenarios such as plugins that load and unload ClassLoader dynamically.

Pay Attention

When you pass in a parameter with null, KavaRef tries to use it as part of the matchable condition (vague condition), and the accuracy may decrease.

The createInstance method does not allow all parameters to be null (the conditions are completely vague), and an exception will be thrown directly because this situation cannot be determined which instance to create.

If there is no single constructor with the most specific parameter types (for example, a null parameter makes multiple constructors indistinguishable), an exception will also be thrown directly instead of choosing one of them at random.

Class Modifier ​

KavaRef also extends Modifier, you can directly use Class.isPublic and other methods to judge a Class modifier.

VariousClass ​

KavaRef provides the VariousClass class to load the Class object with an indeterminate full class name and return the first match successfully.

This feature is usually used for class names in Android apps that are obfuscated by R8.

The following example

kotlin
// Assume that in version A, this class is com.example.a,
// In version B, this class is com.example.b.
val myClass = VariousClass("com.example.a", "com.example.b").load()
// You can also use a method with the suffix OrNull to return null
// instead of throwing an exception if Class is not found.
val myClassOrNull = VariousClass("com.example.a", "com.example.b").loadOrNull()

VariousClass uses the same ClassLoader as the toClass method when loading Class.

Lazy Loading Class Object ​

KavaRef provides the LazyClass class to lazy loading the Class object.

You can load Class when needed, instead of loading it immediately when created, which can solve some Class that need to be loaded when run or run to specific conditions.

The following example

kotlin
// Define a Class that cannot be loaded for null and hosts it to myClass.
val myClass by lazyClass("com.example.MyClass")
// Define a Class that can be loaded for null delay and host it to myClassOrNull.
val myClassOrNull by lazyClassOrNull("com.example.MyClass")
// It can also support incoming VariousClass.
val otherClassOrNull by lazyClassOrNull(VariousClass("com.example.a", "com.example.b"))
// Called and loaded when needed.
myClass.resolve()
myClassOrNull?.resolve()
otherClassOrNull?.resolve()

ClassLoader Extension ​

KavaRef also provides some practical extension methods for ClassLoader.

The following example

kotlin
// Assume that's your ClassLoader.
val classLoader: ClassLoader
// Load a Class and return null if the load fails.
val myClassOrNull = classLoader.loadClassOrNull("com.example.MyClass")
// Determine whether this Class exists in the current ClassLoader.
val isClassExists = classLoader.hasClass("com.example.MyClass")

Array Class Extension ​

In Java, the Class object of an array is a special Class object, and usually we create it as follows.

For example, create a Class object of java.lang.String[].

The following example

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

This is very long to write and is not convenient to maintain, so KavaRef provides a way to simplify this process.

Now, the Class object that creates java.lang.String[] can be written like this.

The following example

kotlin
val arrayClass = ArrayClass(String::class)

Member Extension ​

KavaRef provides some extension methods to simplify operations on Member.

You can set its accessibility using the makeAccessible method on any Member object.

It will take effect if Member is the AccessibleObject type.

The following example

kotlin
// Suppose this is your current Member object.
val method: Method
// Make method is accessible, return whether it is successful.
val isSuccess = method.makeAccessible()

Similarly, KavaRef also extends Modifier, and you can directly use Member.isPublic and other methods to judge a Member modifier.

Type Extension ​

When manipulating types or generic types in Java, you usually need to use the Type interface and its subinterface to handle it.

KavaRef provides some extension methods to simplify operations on Type.

For example, you can convert a Type that meets the requirements to a Class object.

The following example

kotlin
val type: Type
val clazz = type.toClass()
// You can also use a method with the suffix named OrNull to
// return null when the conversion fails instead of throwing an exception.
val clazzOrNull = type.toClassOrNull()

toClass also supports generic arrays (GenericArrayType), and the array dimensions are preserved during conversion.

The following example

kotlin
// Assume this is the Type of List<String>[].
val genericArrayType: GenericArrayType
// This will be java.util.List[].
val arrayClass = genericArrayType.toClass()

Type variables (TypeVariable) and wildcard types (WildcardType) have no definite Class, so converting them will throw an exception.

You can also convert Type that meets the requirements to ParameterizedType object.

The following example

kotlin
val type: Type
val parameterizedType = type.asParameterizedType()
// You can also use a method with the suffix named OrNull to
// return null when the conversion fails instead of throwing an exception.
val parameterizedTypeOrNull = type.asParameterizedTypeOrNull()

You can also use the following method to get the generic parameter array in the superclass, which is often used in some superclass and subclass encapsulation operations.

The following example

kotlin
val myClass: Class<*>
// Get the generic parameter array of myClass superclass.
// If the acquisition fails or cannot be retrieved, the empty array will be returned.
val arguments = myClass.genericSuperclassTypeArguments()

Type Reference Extension ​

In Java, generics are erased after compilation, so you cannot get the generic arguments of an object directly at runtime.

KavaRef provides the TypeRef class, which keeps the complete type information through the generic signature of an anonymous subclass. Its core functionality is referenced from Gson's TypeToken.

It is very simple to use, you can use it like this.

The following example

kotlin
val listStringType = typeRef<List<String>>()
// Get the stored type, which will be List<? extends String>.
val type = listStringType.type
// Get its raw type, which will be List.
val rawType = listStringType.rawType

rawType also supports generic arrays and preserves the array dimensions.

The following example

kotlin
// This will be java.util.List[].
val arrayRawType = typeRef<Array<List<String>>>().rawType
// Multidimensional arrays work too, this will be java.util.List[][].
val nestedArrayRawType = typeRef<Array<Array<List<String>>>>().rawType

In scenarios where you need to pass in Type such as when using Gson, you can implement an extension method with reified generics for this purpose.

The following example

kotlin
val gson = Gson()

inline fun <reified T> T.toJson(): String = gson.toJson(this, typeRef<T>().type)

// Usage
val json = listOf("KavaRef", "is", "awesome").toJson()

Notice

TypeRef holds a Java Type, which does not contain Kotlin nullability, so typeRef<String?>() and typeRef<String>() are equal, and the same goes for List<String?>?.

If you need to distinguish nullability, please use Kotlin's typeOf<T>() to get a KType.

Pay Attention

TypeRef can only be inherited directly, such as object : TypeRef<List<String>>() {}. If you inherit from a subclass of TypeRef, an exception will be thrown when getting type or rawType.

When you create a TypeRef in a generic method without reified, the generic argument will not be replaced with the type passed in by the caller.

The following example

kotlin
fun <T> createTypeRef() = object : TypeRef<T>() {}

val typeRef = createTypeRef<String>()
// You will get the type variable T instead of String.
val type = typeRef.type
// T has no definite Class, so an exception will be thrown here.
val rawType = typeRef.rawType

Types that have their own raw class such as List<T> are not affected, and rawType is still List, but the rawType of T and Array<T> cannot be obtained.

So, please try to use typeRef<T>() in methods with reified generics.

Java Wrapper Classes Extension ​

In Kotlin, you can directly use Boolean::class, Byte::class, etc. to obtain Java's original types boolean and byte instead of their wrapper classes.

If you need to get Java wrapper classes, you need to use the complete java.lang.Boolean::class, java.lang.Byte::class, etc. or use Boolean::class.javaObjectType, Byte::class.javaObjectType.

So, KavaRef provides some type alias to handle Java wrapper classes. Now you only need to prefix J to these types, such as JBoolean::class. It is equivalent to java.lang.Boolean::class, and some types need to be filled in the full name, such as JInteger::class.

Released under the Apache-2.0 License