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

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)]
}
}

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
}

How to nest multiple property delegates in 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) }

How to inherit MutableList in Kotlin?

I am trying to inherit MutableList, and add my own function to it. For example:
class CompositeJob : MutableList<Job> {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
But I got the following error:
Class 'CompositeJob' is not abstract and does not implement abstract member
public abstract val size: Int defined in kotlin.collections.MutableList
How can I inherit MutableList, so I can use its original methods like add() and isEmpty(), and add my own one?
Thanks.
One option other answers don't mention is delegation:
class CompositeJob : MutableList<Job> by mutableListOf() {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
is basically equivalent to
class CompositeJob : MutableList<Job> {
private val impl: MutableList<Job> = mutableListOf()
override fun size() = impl.size()
override fun add(x: Job) { impl.add(x) }
// etc for all other MutableList methods
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
MutableList is an interface - it doesn't implement any of its methods, just declares them. If you want to implement MutableList from scratch, you'll have to implement all 20 of its methods plus the size property, as your error already told you.
You can, however, subclass actual implementations of this interface, for example ArrayList or LinkedList:
class CompositeJob : ArrayList<Job>() {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
Edit: If you're just looking to group coroutine Job instances, you should use a parent Job, a SupervisorJob, and CoroutineScope at this point, instead of collecting jobs like this manually.
Implementation of all the members is not the only way to achieve what you need.
An easier way is create an extension function like this:
fun MutableList<Job>.cancelAllJobs() {
this.forEach { it.cancel() }
}
Now you can call myList.cancelAllJobs() for every MutableList<Job> object.
As I have tested you need to make CompositeJob an abstract as error also suggest. After that, you can able to override add() and isEmpty() from MutableList
So your code looks like as below:
abstract class CompositeJob : MutableList<Job> {
override fun add(element:Job): Boolean
{
}
override fun isEmpty(): Boolean
{
}
}
If you want to avoid inheritance, or if the concrete class methods are marked final, then you can use composition and redirect all method calls which are not overridden. Here's a template:
class MyMutableList<T> : MutableList<T> {
private val list = mutableListOf<T>()
override val size get() = list.size
// MutableList<T>
override fun add(element: T): Boolean = list.add(element)
override fun add(index: Int, element: T) = list.add(index, element)
override fun addAll(elements: Collection<T>): Boolean = list.addAll(elements)
override fun addAll(index: Int, elements: Collection<T>): Boolean = list.addAll(index, elements)
override fun clear() = list.clear()
override fun remove(element: T): Boolean = list.remove(element)
override fun removeAll(elements: Collection<T>): Boolean = list.removeAll(elements)
override fun removeAt(index: Int): T = list.removeAt(index)
override fun retainAll(elements: Collection<T>): Boolean = list.retainAll(elements)
override fun set(index: Int, element: T): T = list.set(index, element)
// List<T>
override fun contains(element: T): Boolean = list.contains(element)
override fun containsAll(elements: Collection<T>): Boolean = list.containsAll(elements)
override fun get(index: Int): T = list.get(index)
override fun indexOf(element: T): Int = list.indexOf(element)
override fun isEmpty(): Boolean = list.isEmpty()
override fun iterator(): MutableIterator<T> = list.iterator()
override fun lastIndexOf(element: T): Int = list.lastIndexOf(element)
override fun listIterator(): MutableListIterator<T> = list.listIterator()
override fun listIterator(index: Int): MutableListIterator<T> = list.listIterator(index)
override fun subList(fromIndex: Int, toIndex: Int): MutableList<T> =
list.subList(fromIndex, toIndex)
}
...of course, this might add an additional layer of indirection, possibly affecting list performance with a slight overhead.

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)

Observable property allowing to add observers at runtime

Via Delegates.observable, Kotlin permits observable properties. I need, however, the ability of adding observers at runtime, as Java's Observable class does.
What I have now, is the following:
import java.util.*
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
class MyObservable<T> (var v: T): java.util.Observable() {
operator fun getValue(thisRef: Any, prop: KProperty<*>) = v
operator fun setValue(thisRef: Any, prop: KProperty<*>, newValue: T) {
v = newValue
setChanged()
notifyObservers()
}
}
fun <T> addObserver(prop: KProperty0<T>, observerFn: (T) -> Unit) =
(prop.apply{ isAccessible = true }.getDelegate() as MyObservable<T>)
.addObserver(Observer({ o, _ -> observerFn((o as MyObservable<T>).v) }))
class ObservableExample {
var i: Int by MyObservable(3)
}
fun main(args: Array<String>) {
val ex: ObservableExample = ObservableExample();
addObserver(ex::i, { println(it) })
ex.i = 7
ex.i = 9
// prints:
// 7
// 9
}
It works, but it feels like reinventing the wheel.
Isn't there a standard solution for this?
If not, is what I've done correct?
A slightly shorter variant of the same idea:
import kotlin.properties.Delegates
typealias IntObserver = (Int) -> Unit
class ObservableExample {
val prop1Observers = mutableListOf<IntObserver>()
var prop1: Int by Delegates.observable(0) { prop, old, new ->
prop1Observers.forEach { it(new) }
}
}
fun main(args: Array<String>) {
val example = ObservableExample()
example.prop1Observers.add({ println(it) })
example.prop1 = 1
example.prop1 = 2
}
The output is as expected. Probably, it is better to make observers property private and add a method to add subscribers but I omitted it for the simplicity.
This is because you starts with a simple example, and can't find the benefits of Kotlin delegated properties.
Kotlin doesn't forcing you to implements any interface to supports delegated properties, yon can using delegated properties in Kotlin just provide getValue & setValue(?) operators. and their visibility even can be private.
Kotlin provided a provideDelegate operator function since 1.1, that let you manage/control how to create a delegate.
The delegate in Kotlin is working in the background, which means it is invisible from the source code point of view, and let the code source treat a delegated properties as a regular properties.
Kotlin delegated properties can easily let you manage java beans without using PropertyEditorSupport in Java, and you don't need to manage the delegate at all in Kotlin, just to notify the changed property only. for example:
val history = mutableMapOf<String, MutableList<Pair<Any?, Any?>>>()
val subject = Subject()
subject.subscribe { event ->
val each = history.getOrPut(event.propertyName) { mutableListOf() }
each.add(event.oldValue to event.newValue)
}
// v--- treat a delegated property as regular property
subject.number = 1
subject.string = "bar"
subject.number = 2
println(history);
// ^--- {"number":[<null,1>,<1,2>], "string": [<null,"bar">]}
Note: the getValue & setValue operator functions private below.
class Subject {
// v--- manage the delegated property internally
var string: String? by this
var number: Int? by this
private val properties by lazy {
mutableMapOf<Any?, Any?>()
}
private val listeners by lazy {
mutableListOf<PropertyChangeListener>()
}
private operator #Suppress("UNCHECKED_CAST")
fun <T : Any?> getValue(self: Any, prop: KProperty<*>): T {
return properties[prop.name] as T
}
private operator
fun <T : Any?> setValue(self: Any,prop: KProperty<*>, newValue: T) {
val event = PropertyChangeEvent(
self,
prop.name,
properties[prop.name],
newValue
)
properties[prop.name] = newValue
listeners.forEach { it.propertyChange(event) }
}
fun subscribe(listener: (event: PropertyChangeEvent) -> Unit) {
subscribe(PropertyChangeListener { listener(it) })
}
fun subscribe(subscriber: PropertyChangeListener) {
listeners.add(subscriber)
}
}