'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 |
