'Kotlin - get all properties from primary constructor

I have created this extension method which gets all properties from a KClass<T>

Extension Method

@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> KClass<T>.getProperties(): Iterable<KProperty1<T, *>> {
    return members.filter { it is KProperty1<*, *> }.map { it as KProperty1<T, *> }
}

Example Usage

data class Foo(val bar: Int) {
    val baz: String = String.EMPTY
    var boo: String? = null
}

val properties = Foo::class.getProperties()

Result

val com.demo.Foo.bar: kotlin.Int

val com.demo.Foo.baz: kotlin.String

var com.demo.Foo.boo: kotlin.String?

How would I modify this extension method to only return properties that are declared in the primary constructor?

Expected Result

val com.demo.Foo.bar: kotlin.Int



Solution 1:[1]

You can take constructor parameters by getting primaryConstructor and then valueParameters, and because primary constructor is not required for kotlin class we can do something like this

inline fun <reified T : Any> KClass<T>.getProperties(): Iterable<KParameter> {
   return primaryConstructor?.valueParameters ?: emptyList()
}

so if we will ask for properties of Foo class

val properties = Foo::class.getProperties()
properties.forEach { println(it.toString()) }

we will get

parameter #0 bar of fun <init>(kotlin.Int): your.package.Foo

and the result is not a KProperty, but a KParameter which may be more aligned to your use case

Solution 2:[2]

val <T : Any> KClass<T>.constructorProperties
    get() =
        primaryConstructor?.let { ctor ->
            declaredMemberProperties.filter { prop ->
                ctor.parameters.any { param ->
                    param.name == prop.name
                    &&
                    param.type == prop.returnType
                }
            }
        } ?: emptyList()

fun <T : Any> KClass<T>.getProperties(): Iterable<KProperty1<T, *>> =
    constructorProperties

This is a rework of previous answers by szymon_prz and Peter Henry, to produce the list of properties declared in the primary constructor, but not:

  • other primary constructor parameters that are not properties
  • other properties that are not primary constructor parameters but have matching names and different types

Unfortunately it will still list properties that are not primary constructor parameters but have the same name and type as one of them.

For example:

// only parameter 'bar' is declared as a property
class Foo(val bar: Int, baz: Int, qux: Int, rod: Int) {
    val zzz = baz            // no parameter zzz 
    val qux = "##($qux)##"   // property is a String but parameter is an Int
    val rod = maxOf(0, rod)  // property and parameter are both Int
}

val ctorProps = Foo::class.constructorProperties
ctorProps.forEach { println(it.toString()) }

will produce:

val Foo.bar: kotlin.Int

val Foo.rod: kotlin.Int

Solution 3:[3]

inline fun <reified T : Any> KClass<T>.getProperties(): List<KProperty<*>> {
    val primaryConstructor = primaryConstructor ?: return emptyList()
    // Get the primary constructor of the class ^

    return declaredMemberProperties.filter {
    // Get the declared properties of the class; i.e. bar, baz, boo
        primaryConstructor.parameters.any { p -> it.name == p.name } 
        // Filter it so there are only class-properties whch are also found in the primary constructor.
    }
}

To summarize, this function basically takes all the properties found in a class and filters them so only ones that are also found in the primary-constructor stay.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 szymon_prz
Solution 2 Joao Trindade
Solution 3