Access CoroutineContext element via Key class - kotlin

In Ktor, I am hoping to implement some way to reference key value pairs inside a coroutineContext, without dragging references to objects around in method parameters. Based on https://proandroiddev.com/demystifying-coroutinecontext-1ce5b68407ad I have written my reference classes:
class MyElement(override val key: CoroutineContext.Key<*>, val value: String) : CoroutineContext.Element
class MyKey: CoroutineContext.Key<MyElement>
... // inside routing:
val key: CoroutineContext.Key<MyElement> = MyKey()
val ele = MyElement(key, "myJWT")
withContext(coroutineContext + ele) {
val notNullEle : MyElement = coroutineContext[ele.key] as MyElement // not null
logger.info(notNullEle.value) // "myJWT"
val shouldNotBeNullEle = coroutineContext[MyKey()]// NULL!
}
val shouldBeNull = coroutineContext[ele.key] // is and should be null
val shouldBeNull2 = coroutineContext[MyKey()] // is and should also be null
When I send ele.key to coroutineContext[ele.key] I get the right element, but when I send a new instance of MyKey along, I get null, so the instance of MyElement is clearly mapped to the instance of the key. That does not work well for my purposes however, as I was hoping to get the instance of MyElement using the class of MyKey, since I would like to be able to, for instance, get the value in a HttpClient in the service layer without having to pass ele.key all the way down the chain. Is it possible?
What I am asking is basically the same as How to make request-bound data globally available in Ktor? which is unfortunately unanswered.

As described in the article you linked, you could consider the CoroutineContext to be a Map with CoroutineContext.Key as its keys.
Given this premise the problem you are having is clear, ele.key != MyKey() or the keys are not equivalent by definition, so they will not return the same entry within the context Map.
This is the reason why most CoroutineContext.Key implementations are object, because that implements the equals method based on the object singleton. You will either need to also use object or correctly implement equals and hashCode.

Related

Kotlin, implement extension var property

It seems that a simple extension property like the following does not work.
var Dog.age = 0;
What is the recommended way to implement this? I have tried the following, and it worked, but this will prevent any Dog object from cleaned up by the Garbage Collector, won't it?
class Dog
{
}
val dogAgeMap=HashMap<Dog, Int>();
var Dog.age:Int
get() = dogAgeMap[this]?: 0;
set(value){ dogAgeMap[this] = value}
class PetShop
{
fun work()
{
val d1 = Dog();
d1.age = 100;
val d2 = Dog();
d2.age = 200;
println("${d1.age}, ${d2.age}");
}
}
fun main(args:Array<String>)
{
PetShop().work();
}
Correct, this will prevent the Dog instances on which the age setter has been called to be GCed inside the scope of where the dogAgeMap is defined. If you defined the Dog.age extension property (and thus dogAgeMap) in a limited scope with a limited (short) lifespan, then you are okay.
However, if that is not the case, and you need the age info all across you application, then age should just be part of the original class definition and you don't ever run into this problem.
Solution in this case
class Dog(val age: Int)
If you need the age information only in one part of your application, then a better way would be to create the lookup (the HashMap) only for that part, or to simply use an enriched class with age (or a wrapper class with age) instead of the Dog class in that part of your application. And when you are done with work there, you clean up the map or the enriched class instances. In that way no instances will leak.
But if you really really want to do it with an extension property across the whole application, and thus you need to keep the reference to the dogAgeMap all the time, then you need to take care of leaking memory if you have a lot of instances that you go through and set their age.
If that is your case you can use a WeakHashMap<Dog, Int> instead. A WeakHashMap only keeps weak references and it won't prevent Dog instances to be GCed (once your strong references are no longer retained).
import java.util.WeakHashMap
val dogAgeMap = WeakHashMap<Dog, Int>()
var Dog.age: Int
get() = dogAgeMap[this] ?: 0
set(value) {
dogAgeMap[this] = value
}
Note however, that WeakHashMap is a Java class and not part of Kotlin core library, so if you use Kotlin for multiplatform, this won't work. In that case you would need a WeakHashMap implementation (library) on each platform.
An alternative way to do this if your data for dogs also contains an ID for each dog, would be to use the ID as the lookup key instead. That would be possible to port to all platforms. The implementation would then change to
// I am using a Long here, but it could be whatever type that
// is small enough to not cause memory concerns, since
// these keys would still exist in memory because a normal HashMap is used.
class Dog(val id: Long) {}
val dogAgeMap = HashMap<Long, Int>()
var Dog.age: Int
get() = dogAgeMap[id] ?: 0
set(value) {
dogAgeMap[id] = value
}

How to test if lateinit var is initialized from outside the class? - Kotlin

This SO post outlines how to test if a lateinit var has been initialized. However, in the example, the lateinit var is conveniently located within the same class.
How do you do the same thing from outside the class? This is the situation I have:
Foo.kt
class Foo {
lateinit var foo: String
}
Bar.kt
class Bar {
fun doSomething() {
val foo = Foo().foo
if (::foo.isInitialized) { // Unsupported [reference to variables aren't supported yet]
Log.i("TAG", "do something")
}
}
}
What's the workaround for this?
If this was going to work, you'd need to do
val foo = Foo()
if (foo::foo.isInitialized)
//...
The way you're doing it, you're trying to get a property reference of your local variable, which isn't a property. That's why the error says "reference to variables aren't supported yet" rather than "backing field not accessible at this point". Also, you'd be accessing the getter of the lateinit property when assigning the local variable, so it would fail if it weren't initialized yet.
But it doesn't work because of compiler limitations. You could simply add a getter
val fooReady: Boolean get() = ::foo.isInitialized
But I would say the design has very poor encapsulation if outside classes need to check whether a particular public property is initialized yet. In my opinion, any use of isInitialized is a code smell to begin with. If you need to guard calls to the getter with isInitialized, you might as well make the property nullable instead. Then you can use the familiar idioms of null checks instead of resorting to reflection, and it will work in a familiar way even for external classes that access it.
If object of another class has to make a decision based on whether or not the property is initialised, then having this property initialised - or answering whether or not it has already been initialised - is a public business capacity of your object and therefore I would recommend you to simply make it a part of your public API via public fun isFooInitialised(): Boolean function that utilises the fact that the object itself can inspect the state of its lateinit properties.

Correct way of implementing LiveData

In the Android docs it shows an example creating a LiveData object as follows:
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
But I have seen code elsewhere that shows it like this:
val currentName: MutableLiveData<String> = MutableLiveData()
Both of these are located in the viewmodel. In the second example, the LiveData model is instantiated when the class is created whereas in the first example, it is only instantiated when the object is first used.
Are both of these cases valid?
Yes, both of these cases are valid. However, there is a distinct difference between the two. When using by lazy it will still set the LiveData object, but it will not set it until the variable is first used. In the case of the second option, it will initialize the LiveData object when parent is created.

How to make a data class singleton in Kotlin?

Assume I have a data class:
data class SensorData(val name: String, val temp : Double)
I create this SensorData object from either an REST service or by internal setter method, whereas name is always populated and temp might be empty.
Further on, I need this SensorData object through several classes, thats I thought of using a singleton.
Obviously I need object keyword as described here, but how can I combine data class object ?
You can use companion object to keep a reference to your data object:
data class SensorData(val name: String, var temp : Double) {
companion object {
#Volatile
#JvmStatic
private var INSTANCE: SensorData? = null
#JvmStatic
#JvmOverloads
fun getInstance(name: String = "default", temp : Double = 0.0): SensorData = INSTANCE ?: synchronized(this) {
INSTANCE ?: SensorData(name, temp).also { INSTANCE = it }
}
}
}
And use it like this:
val name1 = SensorData.getInstance("name", 5.0).name
// Or with default values:
val name2 = SensorData.getInstance().name
I think you got the concept of a Singleton wrong:
"The singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance"
It is not only meant to be used to make it public to all classes, but to limit the number of instances.
A data class is a class to store data, why should it be a Singleton?
Rethink your architecture to make it accessible where you need it.
Don't use a data class as a singleton. That's not what they are designed for. A better approach would be to create a wrapper object around your data class, which handles your SensorData-Object(s). This will also allow you to use multiple SensorData objects (maybe needed in future) or replace the current one with a new one if you poll the REST-Service a second time.
object SensorDataService {
var sensorData: SensorData? = null
}
data class SensorData(val name: String, val temp : Double)
In my opinion, you should rethink your architecture as data class is used to store data so why it should be a singleton? It is not only meant to be used to make it public to all classes but to limit the number of instances.

Kotlin multiple class for data storage

I am developing a simple Android app, that will display an icon of a vehicle and the user can click on the icon to display the vehicle information. I want to load the data dynamically when I build the app i.e. the data will come from an external source including the picture for the icon.
I am new to Kotlin and not sure what to search for to understand a suitable solution. What is the correct way to define the data, is it best to create an class as below then create an array of the class (not sure if this is possible)
public class VehicleSpec()
{
var OEM: String? = null
var ModelName: String? = null
var EngineSize: String? = null
}
Or would be better to create a multiple dimension array and then link the data to the cells?
var VehicleSpec = arrayOf(20,20)
VehicleSpec[0][0] = Null //OEM
VehicleSpec[0][1] = Null //ModelName
VehicleSpec[0][2] = Null //EngineSize
What is the best way to set up the data storage, is there any good references to understand how this should be setup?
What is the correct way to define the data, is it best to create an class as below then create an array of the class
Using an array for the properties of an object is not making the full use of the type safety you have in Kotlin (and even Java for that matter).
If what you want to express is multiple properties of an object, then you should use a class to define those properties. This is especially true if the properties have different types.
There is no performance difference between an array and a class, because you'll get a reference to the heap in both cases. You could save on performance only if you convert your multi-dimensional array approach to a single-dimension array with smart indexing. Most of the time, you should not consider this option unless you are handling a lot of data and if you know that performance is an issue at this specific level.
(not sure if this is possible)
Defining lists/arrays of classes is definitely possible.
Usually, for classes that are only used as data containers, you should prefer data classes, because they give you useful methods for free, and these methods totally make sense for simple "data bags" like in your case (equals, hashcode, component access, etc.).
data class Vehicle(
val OEM: String,
val ModelName: String,
val EngineSize: String
)
Also, I suggest using val instead of var as much as possible. Immutability is more idiomatic in Kotlin.
Last but not least, prefer non-null values to null values if you know a value must always be present. If there are valid cases where the value is absent, you should use null instead of a placeholder value like empty string or -1.
First at all, using the "class aprocah" makes it easy for you to understand and give you the full benefits of the language itself... so dont dry to save data in an array .. let the compiler handle those stuff.
Secondly i suggest you have maybe two types (and use data classes ;-) )
data class VehicleListEntry(
val id: Long,
val name: String
)
and
data class VehicleSpec(
val id: Long,
val oem: String = "",
val modelName: String = "",
val engineSize: String = ""
)
from my perspective try to avoid null values whenever possible.
So if you have strings - which you are display only - use empty strings instead of null.
and now have a Model to store your data
class VehicleModel() {
private val specs: MutableMap<Long, VehicleSpec> = mutableMapOf()
private var entries: List<VehicleListEntry> = listOf()
fun getSpec(id: Long) = specs[id]
fun addSpec(spec: VehicleSpec) = specs[spec.id] = spec
fun getEntries(): List<VehicleListEntry> = entries
fun setEntries(data: List<VehicleListEntry>) {
entries = data.toMutableList()
}
}
You could also use a data class for your model which looks like
data class VehicleModel(
val specs: MutableMap<Long, VehicleSpec> = mutableMapOf(),
var entries: List<VehicleListEntry> = listOf()
)
And last but not least a controller for getting stuff together
class VehicleController() {
private val model = VehicleModel()
init{
// TODO get the entries list together
}
fun getEntries() = model.entries
fun getSpec(id: Long) : VehicleSpec? {
// TODO load the data from external source (or check the model first)
// TODO store the data into the model
// TODO return result
}
}