1. Introduction

In Kotlin, reflection allows us to dynamically inspect, manipulate, and interact with classes, fields, and methods at runtime. As developers, we sometimes need to retrieve all field names of a class dynamically.

In this tutorial, we’ll investigate various techniques to get all field names of a class in Kotlin using reflection.

2. Using the members Property

Our first approach to getting all the field names of a class involves using the members property of KClass. This returns a list of all the members of the class, including fields and methods. We can filter these members to extract only the properties:

fun getAllFieldNamesUsingMembersProperty(clazz: KClass<*>): List<String> {
    return clazz.members
      .filter { it is KProperty<*> }
      .map { it.name }
}

In this method, we use the members property of a KClass to retrieve all its members. Then, we map each member or property to its name using the name property. This yields a list containing the names of all fields within the class.

To demonstrate these techniques, we’re going to be using this TestClass with an abstract Identifiable base class for testing:

abstract class Identifiable(open val id: Int)
class TestClass(val name: String, val age: Int, override val id: Int): Identifiable(id)

Now, let’s test our helper method for correctness:

@Test
fun `obtain all field names using members method`() {
    val fieldNames = getAllFieldNamesUsingMembersProperty(TestClass::class)

    assertEquals(listOf("age","id", "name"), fieldNames)
}

Notably, we retrieve all fields declared in TestClass and Identifiable with this approach.

3. Using the memberProperties Property

This approach is similar to the first approach, but instead of using the members property of the KClass class, we use the memberProperties property of KClass. This property allows direct access to fields declared in the class, skipping other class attributes such as functions:

fun getAllFieldNamesUsingClassMemberProperties(clazz: KClass<*>): List<String> {
    return clazz.memberProperties
      .map { it.name }
}

We use the memberProperties property to retrieve all properties in the class. Similarly, we then map each property to its name using the name property. Finally, we obtain a list containing the names of all accessible fields within the class.

Let’s test the approach:

@Test
fun `obtain all field names using Class members property `() {
    val fieldNames = getAllFieldNamesUsingClassMemberProperties(TestClass::class)

    assertEquals(listOf("age", "id", "name"), fieldNames)
}

Our test shows how we use our helper method to retrieve all field names of a Kotlin class.

4. Using the declaredMemberProperties Property

Kotlin’s Reflection API provides us with the declaredMemberProperties property, which we can use to obtain all field names of a class. This property returns a collection of all properties declared directly in a Kotlin class:

fun getAllFieldNamesUsingDeclaredMemberPropertiesMethod(clazz: KClass<*>): List<String> {
    return clazz.declaredMemberProperties
      .map { it.name }
}

This helper method takes a KClass as input and returns a list of field names belonging to that class. We use Kotlin’s Reflection API to access the declaredMemberProperties property of the provided class, which retrieves all the properties declared within it. Finally, we map each property to its name using the name property again. This results in a list containing the names of all fields within the class:

@Test
fun `obtain all field names using declaredMemberProperties method`() {
    val fieldNames = getAllFieldNamesUsingDeclaredMemberPropertiesMethod(TestClass::class)

    assertEquals(listOf("age","id", "name"), fieldNames)
}

With this specific example, we still receive the same list of properties, because the id property is still declared in TestClass. Next, we’ll look at an example where the abstract property is not declared on the base class.

5. Differences Between declaredMemberProperties and memberProperties

While both declaredMemberProperties and memberProperties are useful for obtaining properties of a class in Kotlin’s Reflection API, it’s important to understand the distinction between them.

The declaredMemberProperties property only retrieves the properties that are explicitly declared in the class. It doesn’t include properties inherited from parent classes.

On the other hand, the memberProperties property retrieves all properties associated with the class, including inherited properties.

To illustrate this difference, let’s consider the class Person and its parent class Human:

open class Human(val gender: String)

class Person(val name: String, val age: Int): Human("male")

The Human class declares a single property gender, while the Person class inherits from the Human class and declares two additional properties, name and age. We intentionally don’t declare the gender property on Person:

fun `show difference between declaredMemberProperties and memberProperties property`() {
    val declaredProperties = getAllFieldNamesUsingDeclaredMemberPropertiesMethod(Person::class)
    val allProperties = getAllFieldNamesUsingClassMemberProperties(Person::class)

    assertEquals(listOf("age", "name"), declaredProperties)
    assertEquals(listOf("age", "name", "gender"), allProperties)
}

We observe that declaredMemberProperties only retrieves the properties declared within the Person class (name and age), while memberProperties includes the inherited property gender from the Human superclass.

6. Using Java’s Reflection

Finally, we can also directly use Java reflection from Kotlin. The Class.getDeclaredFields() property is part of Java’s Reflection API, and we can use it to obtain all field names of a class in Kotlin. This works because Kotlin interoperates seamlessly with Java:

fun getAllFieldNamesUsingJavaReflection(clazz: KClass<*>): List<String> {
    return clazz.java.declaredFields
      .map { it.name }
}

We access the Java Class from the original KClass. Next, we use the getDeclaredFields() method to retrieve all fields within the class. Finally, we map each field to its name using the name property.

Let’s test the approach:

@Test
fun `obtain all field names using java reflection API`() {
    val fieldNames = getAllFieldNamesUsingJavaReflection(TestClass::class)

    assertEquals(listOf("name", "age", "id"), fieldNames)
}

7. Conclusion

In this article, we’ve learned how we can use different techniques to obtain the names of all the fields of a class in Kotlin using reflection. We used KClass properties such as members, memberProperties, and declaredMemeberProperties from Kotlin’s native Reflection API. Finally, we saw how Kotlin can seamlessly interoperate with Java’s reflection API through the getDeclaredFields() method to achieve the same goal.

Each approach has its advantages and limitations. We must consider our use case and requirements before selecting a particular approach. Overall, Kotlin’s Reflection API provides an incredible tool that enables us to inspect, manipulate, and interact with classes at runtime, which can be incredibly useful in a variety of scenarios.

As always, the code examples in this article can be found over on GitHub.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments