Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

It turns out that method references like this are very badly implemented: the people that brought you lobotomized generics, Silly <> syntax and lambda's without functions also managed to make a mess of this, obviously. A method reference is nothing more than the compiler generating some sad code that makes an implementation of an interface on the fly. The interface implemented is one of the java.util.function, in this case an implementation of Function<Invoice, BigDecimal>. As this is a very bare bones type it knows nothing: not even the name of the function. So while this allows access to the getter we cannot use it to calculate how to reach the setter. Pretty incredibly dumb if you ask me.

Generating something?

...

The type of a property reference is at least KProperty1 which is typeful:

Code Block
val name2: KProperty1<InvoiceKMutableProperty1<Invoice, BigDecimal> = Invoice::amount

If the property actually is writable then this can also be a KMutableProperty1 class which contains a set method to set a value.This is typeful, and we can use it to properly restrict values to a method:

Code Block
fun <T, V> eq(property: KMutableProperty1<T, V>, value: V) ....

eq(Invoice::amount, "Hello") // this will not compile because "hello" is not BigDecimal.


One thing to be aware of though: read-only properties (val) return a KProperty1 instead, and that one has an issue: its value parameter is defined as "out". Because of this using that type as a restriction does not really restrict:

Code Block
fun <T, V> eq(property: KProperty1<T, V>, value: V)


eq(Invoice::amount, "Hello")  // This does compile, sadly enough 8-/


The KProperty1 type contains a method to return the property name - which is very useful when you want to use it to, for instance, generate a SQL query based on that name. It also contains get and set methods which will let you get the value and set the value of the property given an instance of Invoice. This is a pretty great implementation because it allows most of what we want:

...

Warning

While property references exist for Kotlin classes that have them, they do not exist for Java classes that have Java's "properties". This is a bit odd, because Kotlin does support accessing Java's properties using the name of the property. This does mean that using Kotlin's property references only works for Kotlin classes. Pity.

But all is not well, sadly enough..

Warning

The below seems to be incorrect: the issue seems to be caused by read-only properties.... Under investigation (wink)

There is one thing however that does not work with Kotlin properties and that is the type safeness, sadly enough...

Take the following example: we want to make something to write typesafe queries, so we have a class with a method eq() like this:

Code Block
fun <T, V> eq(property: KProperty1<T, V>, value: V) {....

The idea, of course, is that when the property is of type String that the value must be that too - or a subclass. This works fine in Java, but in Kotlin something unexpected happens. This code compiles fine (with name being a String property):

Code Block
eq(Invoice::name, 1234)

The reason is the definition of KProperty1:

Code Block
public interface KProperty1<T, out R> : kotlin.reflect.KProperty<R>, (T) -> R

The R parameter, representing the value (the return value, hence the R) is defined as an out parameter. Out means that it is safe to put that R into any superclass: it can be put in an Object, no problem. But this also means that in our "eq" method the V property is defined as an out one- so it will accept anything that shares some common base class with the actual type, like Object....

...