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
}