Dj Sushi
Dj Sushi

Reputation: 646

Is it possible to pass class properties as function parameters in Kotlin?

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

Answers (2)

dcahn
dcahn

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

Ryan M
Ryan M

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

Related Questions