Reputation: 646
I have a
class Foo {
lateinit var property1: String
lateinit var property2: Int
lateinit var property3: Long
}
Then is it possible to pass a property of the class in a function like this one?
fun bar(fooProperty: FooProperty) {
println(
when(fooProperty) {
Foo.property1 -> "Property1"
Foo.property2 -> "Property2"
Foo.property3 -> "Property3"
}
)
}
This is invalid code however. I was just wondering if that is achievable.
Upvotes: 4
Views: 1662
Reputation: 41
There are a few options. The simplest approach is to use a wrapper class (#1); you can even use a sealed class (#2) so that when
doesn't require an else
branch. The downside of using a wrapper class is that wrapper classes can be far less efficient than using primitive types. That said, primitive types cannot be lateinit
and a delegated property would probably be necessary in this use case, so a wrapper class could be a good idea.
The best answer is probably Option #3 - using inline classes. Inline classes should be optimised as if you were using the underlying primitive type, while providing the full benefits of OOP (unlike just using reflection). The downside of this is that they can't be sealed (so they'll require an else
branch when using them with when
) and that they're experimental.
Note that the code in the questions has a number of issues including missing is
and else
in the when
expression and using lateinit
with a primitive type.
Option #1:
class Foo {
lateinit var property1: Foo.Property1
lateinit var property2: Foo.Property2
lateinit var property3: Foo.Property3
abstract class FooProperty
class Property1(val value:String):FooProperty()
class Property2(val value:Int):FooProperty()
class Property3(val value:Long):FooProperty()
}
fun bar(fooProperty: Foo.FooProperty) {
println(
when(fooProperty) {
is Foo.Property1 -> "Property1"
is Foo.Property2 -> "Property2"
is Foo.Property3 -> "Property3"
else -> error("Impossible")
}
)
}
Option #2:
class Foo {
lateinit var property1: FooProperty1
lateinit var property2: FooProperty2
lateinit var property3: FooProperty3
}
sealed class FooProperty
class FooProperty1(val value: String) : FooProperty()
class FooProperty2(val value: Int) : FooProperty()
class FooProperty3(val value: Long) : FooProperty()
fun bar(fooProperty: FooProperty) {
println(
when (fooProperty) {
is FooProperty1 -> "Property1"
is FooProperty2 -> "Property2"
is FooProperty3 -> "Property3"
}
)
}
Option #3:
inline class FooProperty1(val value:String)
inline class FooProperty2(val value:Int)
inline class FooProperty3(val value:Long)
class Foo {
var property1: FooProperty1 by Delegates.notNull()
var property2: FooProperty2 by Delegates.notNull()
var property3: FooProperty3 by Delegates.notNull()
}
fun bar(fooProperty: Any) {
println(
when(fooProperty) {
is FooProperty1 -> "Property1"
is FooProperty2 -> "Property2"
is FooProperty3 -> "Property3"
else -> error("")
}
)
}
Upvotes: 0
Reputation: 20207
Yes, this is possible, just use ClassName::propertyName
to get a reference to the property and KProperty1<ClassName, *>
as the parameter type.
So, a full working example with your example class (changed a bit to make the class compile) would look like:
import kotlin.reflect.KProperty1
class Foo {
lateinit var property1: String
var property2: Int = 0
var property3: Long = 0
}
fun bar(fooProperty: KProperty1<Foo, *>) {
println(
when(fooProperty) {
Foo::property1 -> "Property1"
Foo::property2 -> "Property2"
Foo::property3 -> "Property3"
else -> throw IllegalArgumentException("Not a known property")
}
)
}
fun main() {
bar(Foo::property2)
}
This prints
Property2
Upvotes: 9