Emulating Java synthetic property references


#1

I’ve been reading about Kotlin for a while, but have only recently started to use it for testing Java code. The interop with Java is great, but I quickly blundered into the fact that Kotlin won’t yet let you use property reference syntax for synthetic Java properties; only directly accessible fields.

This limitation is unfortunate for a number of reasons, but it’s disappointing for my testing use case because bound property references are an appealing way to capture the property name for message generation when making test assertions.

with (person) {
	::name shouldEqual "a name"
}

With some further tinkering, I’ve found a way to insert extension properties that delegate reflectively, but this obviously isn’t as good as native support.

class SyntheticPropertyDelegate<R : Any>(type: KClass<R>) : ReadWriteProperty<R, Any> {
	private val properties: Map<String, PropertyDescriptor> = 
		Introspector.getBeanInfo(type.java).propertyDescriptors
			.fold(mutableMapOf(), { m, p -> m[p.name+"_"] = p; m })

	override fun getValue(thisRef: R, property: KProperty<*>): Any = 
		properties[property.name]?.readMethod?.invoke(thisRef) as Any

	override fun setValue(thisRef: R, property: KProperty<*>, value: Any) {
		properties[property.name]?.writeMethod?.invoke(thisRef, value)
	}

	@Suppress("UNCHECKED_CAST")
	operator fun <T> invoke() = this as ReadWriteProperty<R, T>
}

val delegate = SyntheticPropertyDelegate(Person::class)
val Person.name_ by delegate<String>()
var Person.age_ by delegate<Int>()

...
with (person) {
	::name_ shouldEqual "a previously set name"
	age = 42
	::age_ shouldEqual 42
}

The underscored extension property doesn’t conflict with the synthetic property which Kotlin knows about but won’t let you reference. Since the extension property is a Kotlin property, references work fine. It requires statically declaring a bunch of extension properties though in a way that’s not statically verifiable.

This is mostly just playing around with the language, but I was wondering if I’d missed anything that could simplify this further. The basic goal is to concisely refer to the property name and value so that Kluent-style infix assertions have both pieces of information when reporting on a failure.