How to nest multiple property delegates in Kotlin - kotlin

I've come accross a case where I want to "chain" mutliple delegates (piping the output of one into the other).
This seems to be possible:
private val errorLogList by listSO(listOf<StateObject<Luxeption>>(), SODest.NONE, publicSOAccessRights())
val errorLog: StateObject<List<StateObject<Luxeption>>> by addToSet(errorLogList)
However, this does not look too well :). I'd like to do it in one line like this:
val errorLog: StateObject<List<StateObject<Luxeption>>> by addToSet(
listSO(listOf<StateObject<Luxeption>>(), SODest.NONE, publicSOAccessRights())
)
My question: Is this type of creating properties through delegates possible in Kotlin?
Here are both implementations of my delegates:
addToSet:
open class ChildSOReturner {
val set: Set<StateObject<*>> = setOf()
inline fun <reified T> addToSet(so: T) = object: ReadOnlyProperty<Any?, T> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (thisRef is T) {
set.plus(so)
return so
} else throw IllegalArgumentException()
}
}
}
listSo:
fun <O> listSO(
initialState: List<StateObject<O>>,
soDest: SODest,
soAccessRights: SOAccessRights
) = object : ReadOnlyProperty<Any?, StateObject<List<StateObject<O>>>> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): StateObject<List<StateObject<O>>> {
val meta = SOMeta(SOId(property.name), soDest, soAccessRights)
return StateObjectList(initialState, meta)
}
}

It turned out to be quite tricky, but possible (unless I am missing something, and it isn't tested but the idea should work):
fun <T, U, V> composeProperties(prop: ReadOnlyProperty<T, U>, f: (U) -> ReadOnlyProperty<T, V>) : ReadOnlyProperty<T, V> {
var props = mutableMapOf<Pair<T, KProperty<*>>, ReadOnlyProperty<T, V>>()
return object : ReadOnlyProperty<T, V> {
override operator fun getValue(thisRef: T, property: KProperty<*>): V {
val prop1 = props.getOrPut(Pair(thisRef, property)) {
f(prop.getValue(thisRef, property))
}
return prop1.getValue(thisRef, property)
}
}
}
And then to use
val errorLog: ... by composeProperties(listSO(...)) { addToSet(it) }

Related

Why do I assign data directly to MutableState variable?

I was told that MutableState just like MutableLiveData in Kotlin, and MutableState fit Compose, MutableLiveDataenter code here fit XML layout.
In Code A, I need to assign data to bb.value, but why do I assign directly to aa ?
Code A
private var aa by mutableStateOf(-1)
private var bb= MutableLiveData<Int>(-1)
fun onEditDone() {
aa = 2
bb.value = 2
}
It's because of Kotlin's delegation feature where you delegate values using by keyword.
Simple implementation for remember and mutableState, to display how it works when you build something similar to that, is as
// Delegation Functions for setting and getting value
operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
this.value = value
}
/*
* State
*/
interface State<out T> {
val value: T
}
interface MutableState<T> : State<T> {
override var value: T
}
class MutableStateImpl<T>(value: T) : MutableState<T> {
override var value: T = value
}
fun <T> mutableStateOf(value: T): MutableState<T> = MutableStateImpl(value)
/*
* Remember
*/
inline fun <T> remember(calculation: () -> T): T {
return calculation()
}
And you can use it as
fun main() {
val isSelected: MutableState<Boolean> = remember { mutableStateOf(true) }
isSelected.value = false
var selected by remember { mutableStateOf(false) }
selected = false
}

Migrate RX to Coroutines

I am starting to learn Coroutines
what this code will look like on Coroutines ?
I would like to leave RX
and start using Coroutines
open class DataState<out T>(val newState: T, val oldState: T?, val json: String)
object EventBus {
private val dataPublisher: MutableMap<String, PublishSubject<DataState<Any>>> = mutableMapOf()
inline fun <reified T : Any> fire(event: DataState<T>) = fire(T::class.java.name, event)
fun <T : Any> fire(clazz: Class<T>, event: DataState<T>) = EventBus.fire(clazz.name, event)
fun <T : Any> fire(className: String, event: DataState<T>) {
synchronized(this) {
dataPublisher[className]?.onNext(event)
}
}
inline fun <reified T : Any> on(): Observable<DataState<T>> = on(T::class.java)
fun <T> on(dataType: Class<T>): Observable<DataState<T>> {
synchronized(this) {
var pub = dataPublisher[dataType.name]
if (pub == null) {
pub = PublishSubject.create()
dataPublisher[dataType.name] = pub
}
return pub.ofType(DataState::class.java) as Observable<DataState<T>>
}
}
fun reset() {
synchronized(this) {
dataPublisher.values.forEach { it.onComplete() }
dataPublisher.clear()
}
}
}
I really have no idea how to do the same thing with Coroutines, but I'm willing to entertain the possibility that it will be an improvement. Could someone show me how this would be done with Coroutines, and explain what's better about the Coroutines way?

Property getter simplification via delegation

I have many properties that follow this pattern, basically the only things that change from the template below are:
the initialized value
the property name
code
var foo: Double = 0.0
get() {
update()
return field
}
var foo2: Double = 1.23
get() {
update()
return field
}
question
is there any way that I can use delegation to to simplify (reduce the verbosity of) the code?
Sure
private fun <T> publishingDelegate(value: T): ReadWriteProperty<Any?, T> = object: ReadWriteProperty<Any?, T> {
private var initValue = value
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
update()
return initValue
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
update()
initValue = value
}
}
var foo: Double by publishingDelegate(0.0)
var foo2: Double by publishingDelegate(1.23)

Not nullable Mutable Map

Java : 1.8.0_102
Kotlin: 1.0.4
I'm trying to create a map where you can do something like map["key"] += 5 similar to javascript.
Kotlin already has withDefault that solves one part of this, but map's get function still returns a nullable value, so i proceeded to make my own implementation of this inspired by withDefault
interface NonNullableMutableMap<K,V> : MutableMap<K,V> {
override fun put(key: K, value: V): V
override fun get(key: K): V
}
fun <K,V> MutableMap<K,V>.withoutNullValues(default: () -> V): NonNullableMutableMap<K, V> {
return NonNullableMapWrapper(this, default)
}
class NonNullableMapWrapper<K,V>(val map: MutableMap<K,V>, val default: () -> V) : NonNullableMutableMap<K,V> {
override fun put(key: K, value: V): V = map.put(key, value) ?: default()
override fun get(key: K): V {
val value = map.getOrPut(key, default)
return value
}
override val size: Int get() = map.size
override fun containsKey(key: K): Boolean = map.containsKey(key)
override fun containsValue(value: V): Boolean = map.containsValue(value)
override fun isEmpty(): Boolean = map.isEmpty()
override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() = map.entries
override val keys: MutableSet<K> get() = map.keys
override val values: MutableCollection<V> get() = map.values
override fun clear() {
map.clear()
}
override fun putAll(from: Map<out K, V>) {
map.putAll(from)
}
override fun remove(key: K): V {
return map.remove(key) ?: default()
}
}
I created the following unit test to test it
class NonNullableMapTest {
#Test
fun notNullableTest() {
val map = HashMap<String, Long>().withoutNullValues { 0 }
map["first"] += 10L
map["second"] -= 10L
assertThat(map["first"]).isEqualTo(10L)
assertThat(map["second"]).isEqualTo(-10L)
assertThat(map["third"]).isEqualTo(0L)
}
}
But i'm getting the following error when i run the test:
tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest
java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsJVMKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class foo.bar.NonNullableMapTest
Any idea how to resolve this issue?
This looks like a bug to me. I recommend reporting it at Kotlin (KT) | YouTrack.
One way to workaround it is by explicitly defining set on your NonNullableMutableMap interface. e.g.:
interface NonNullableMutableMap<K, V> : MutableMap<K, V> {
override fun put(key: K, value: V): V
override fun get(key: K): V
operator fun set(key: K, value: V) {
put(key, value)
}
}
Regarding to the runtime error you get, there is currently a bug in how += operator gets compiled for inline MutableMap.set extension function: https://youtrack.jetbrains.com/issue/KT-14227
The workaround is not to use +=:
map["first"] = map["first"] + 10L

Kotlin - How to make a property delegate by map with a custom name?

I'm trying to get my head around property delegates, and I have an interesting use case. Is it possible to have something like this:
class MyClass {
val properties = mutableMapOf<String, Any>()
val fontSize: Any by MapDelegate(properties, "font-size")
}
That would allow me to store fontSize using the map as a delegate, but with a custom key (i.e. "font-size").
The specific use case if for storing things like CSS property tags that can be accessed through variables (fontSize) for use in code, but can be rendered properly when iterating through the map (font-size: 18px;).
The documentation on the delegated properties is a good source of information on the topic. It probably is a bit longer read than the following examples:
fun <T, TValue> T.map(properties: MutableMap<String, TValue>, key: String): ReadOnlyProperty<T, TValue> {
return object : ReadOnlyProperty<T, TValue> {
override fun getValue(thisRef: T, property: KProperty<*>) = properties[key]!!
}
}
class MyClass {
val properties = mutableMapOf<String, Any>()
val fontSize: Any by map(properties, "font-size")
}
You can ease up things a little bit and avoid typing the CSS property name by converting Kotlin property names to CSS attributes equivalents like so:
fun <T, TValue> map(properties: Map<String, TValue>, naming:(String)->String): ReadOnlyProperty<T, TValue?> {
return object : ReadOnlyProperty<T, TValue?> {
override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
}
}
object CamelToHyphen : (String)->String {
override fun invoke(camelCase: String): String {
return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camelCase)
}
}
fun <T, TValue> T.cssProperties(properties: Map<String,TValue>) = map(properties, CamelToHyphen)
class MyClass {
val properties = mutableMapOf<String, Any>()
val fontSize: Any? by cssProperties(properties)
}
The above sample uses Guava's CaseFormat.
If you'd like to have mutable property your delegate will have to implement setter method:
fun <T, TValue> map(properties: MutableMap<String, TValue?>, naming: (String) -> String): ReadWriteProperty<T, TValue?> {
return object : ReadWriteProperty<T, TValue?> {
override fun setValue(thisRef: T, property: KProperty<*>, value: TValue?) {
properties[naming(property.name)] = value
}
override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
}
}