Reflection utilities for Kotlin applications.
libGDX provides its own cross-platform reflection API via static ClassReflection and ArrayReflection.
While standard Java and Kotlin reflection APIs work on most platforms, libGDX API ensures compatibility
with backends such as HTML/WebGL and other third-party backends. However, with their frequent Class
parameter usage, the provided static methods are inconvenient to use from within Kotlin. Additionally, due to
Kotlin's unchecked exceptions, libGDX by itself does not warn the users about reflection API usage, which can
cause various runtime issues.
Instead of relying on static methods, ktx-reflect provides an inlined ReflectedClass wrapper that
allows to invoke ClassReflection and ArrayReflection methods with improved API. Since ReflectedClass
is an inlined value class, the overhead is very similar to using libGDX reflection APIs directly.
ReflectedClass can be created either by using one of its constructors with Class or KClass parameters,
or by reflect<Type> and reflect(String) top-level functions.
ReflectedClass exposes the following ClassReflection methods as read-only properties:
simpleNameisMemberClassisStaticisArrayisPrimitiveisEnumisAnnotationisInterfaceisAbstractcomponentTypeconstructorsmethodsdeclaredMethodsfieldsdeclaredFieldsannotationsdeclaredAnnotationsinterfacesenumConstants
ReflectedClass exposes the following ClassReflection methods with improved API including reified types and
KClass parameters:
isInstanceisAssignableFromnewInstancegetConstructorgetDeclaredConstructorgetMethodgetDeclaredMethodgetFieldgetDeclaredFieldisAnnotationPresentgetAnnotationgetDeclaredAnnotation
Additional ReflectedClass utilities include:
constructorproperty allows to extract the only class constructor if it is available.newArrayInstancemethod allows to create a type array of the given size usingArrayReflection.
ReflectedClass factory methods:
reflect<Type>()top-level function allows to wrap a selected class with aReflectedClass.reflect(KClass<Type>)top-level function allows to wrap a selected Kotlin class with aReflectedClass.reflect(Class<Type>)top-level function allows to wrap a selected Java class with aReflectedClass.reflect(String)top-level function allows to find a selected class by qualified name and wrap it with aReflectedClass.
Other utilities include:
Reflectionannotation, which requires opt-in, allows marking functionalities that rely on reflection.Annotation.get<Type>extension method with a reified type allowing to get an instance of the annotation.
Obtaining a class wrapper:
import ktx.reflect.ReflectedClass
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass
@OptIn(Reflection::class)
fun getWrappedClass(): ReflectedClass<MyClass> {
return reflect<MyClass>()
}Obtaining a class wrapper for a Java Class:
import ktx.reflect.ReflectedClass
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass
@OptIn(Reflection::class)
fun getWrappedClass(): ReflectedClass<MyClass> {
return reflect(MyClass::class.java) // or ReflectedClass(MyClass::class.java)
}Obtaining a class wrapper by name:
import ktx.reflect.ReflectedClass
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass
@OptIn(Reflection::class)
fun getWrappedClass(): ReflectedClass<*> {
return reflect("my.package.MyClass")
}Checking class properties:
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass
@OptIn(Reflection::class)
fun getClassProperties() {
val myClass = reflect<MyClass>()
myClass.isAbstract
myClass.isStatic
myClass.simpleName
myClass.methods
myClass.fields
myClass.annotations
}Creating a new instance of class with a no-argument constructor with reflection:
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass
@OptIn(Reflection::class)
fun createNewInstance(): MyClass {
return reflect<MyClass>().newInstance()
}Creating a new instance of class with a specific constructor selected by parameter types:
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass(val a: String)
@OptIn(Reflection::class)
fun createNewInstance(): MyClass {
// Note that a class cast is necessary, as libGDX wrappers strip types:
return reflect<MyClass>().getConstructor(String::class).newInstance("a") as MyClass
}Calling a method via reflection:
import ktx.reflect.Reflection
import ktx.reflect.reflect
class MyClass {
fun myMethod(parameter: String): String = parameter.uppercase()
}
@OptIn(Reflection::class)
fun invokeMethod(instance: MyClass): String {
val method = reflect<MyClass>().getDeclaredMethod("myMethod", String::class)
return method.invoke(instance, "Argument") as String
}Getting an annotation instance from a class:
import ktx.reflect.Reflection
import ktx.reflect.get
import ktx.reflect.reflect
annotation class MyAnnotation(val value: String)
@MyAnnotation("Hello!")
class MyClass
@OptIn(Reflection::class)
fun getAnnotation(): MyAnnotation? {
// The `get()` call unwraps the annotation from the libGDX wrapper.
return reflect<MyClass>().getAnnotation<MyAnnotation>()?.get()
}Propagating the reflection usage warning via the @Reflection annotation:
import ktx.reflect.Reflection
import ktx.reflect.reflect
interface MyInterface
/** This method uses reflection and requires opt-in! */
@Reflection
fun isAbstract(): Boolean {
// Note that inside @Reflection-annotated methods and classes
// you can freely use reflection API:
return reflect<MyInterface>().isAbstract
// However, any other code using this function would either
// need opt-in or use the same annotation.
}Refer to the libGDX documentation for more reflection usage examples.
If cross-platform compatibility is not a concern, Kotlin and Java reflection APIs can be used directly.