Groovy Properties Assertion Fails - properties

Here is the content of my groovy file:
def KEY = "a"
Properties myProp = new Properties()
myProp[KEY] = "b"
assert(myProp[KEY] == myProp.getProperty(KEY))
Properties results = new Properties(myProp)
assert(results[KEY] == results.getProperty(KEY))
I expected both asserts to pass but only the first assert passes and the second assert fails.
Any explanation to this is greatly appreciated. Thanks!

So, when the docs say "creates an empty property list", that's what it does:
println(results)
>>> [:]
Check out what getProperty does:
Searches for the property with the specified key in this property list. If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked. The method returns null if the property is not found.
Leading to the conclusion that the [] (getAt) does not search the default property list.
We can pursue this to see how Groovy implements getAt:
public static <K,V> V getAt(Map<K,V> self, K key) {
return self.get(key);
}
So it's calling the underlying Hashtable's get method, which knows nothing about the default property list--defaults are part of Properties, not Hashtable:
println(results.getProperty(KEY))
>>> b
println(results.getAt("a"))
>>> null
println(results.get("a"))
>>> null
Is this "correct" behavior? Likely not--maybe a Properties.getAt would be in order.

Related

Kotlin: Unusual behavior when dealing with sets

I have a simple data class which stores x and y coordinates of a position. My use case is that a single object of this class will be created and updated, and I need to maintain a set of unique coordinates.
I've simplified my use case in the following code where adding the pos object directly to the set vs passing the copy of the object result in different behavior (please see the comment in the code).
My initial hunch was that it could be because Java/Kotlin is passing the object by reference and the Set.add compares on reference. However, that doesn't seem to be true, if I set the pos.x or pos.y to any other value then the set.contains method returns false.
Question:
If the comparison is by reference then why does it fail when setting to a value other than what is given in the below code? If comparison is by hash code then why does the setByCopy not return true in the original case?
data class Pos(var x: Int = 0, var y: Int = 0)
fun main() {
val pos = Pos(0, 0)
val set = mutableSetOf<Pos>()
val setByCopy = mutableSetOf<Pos>()
pos.x = -9
pos.y = -6
set.add(pos)
setByCopy.add(pos.copy())
println(pos.hashCode())
pos.x = -8
pos.y = -37
// setting pos.y to any other value (e.g -35) will cause set.contains(pos) to return false.
println(set.contains(pos)) // true, but expected false.
println(setByCopy.contains(pos)) // false
}
As usual, modifying an element that's already in a set produces undefined behavior. This is not explicitly documented in Kotlin, but carries over from Java, where it's documented:
Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.
This means that anything can happen: it can work or not work randomly.
You're creating two objects, pos and then a separate one we'll call pos2. When you invoke copy() on an instance of a data class, you get a completely separate instance with its properties initialised to the same data.
Then you add each instance to a separate Set. Even though set contains pos and setByCopy contains pos2, if you call setByCopy.contains(pos) then that will return true because of how equality works for sets and data classes:
boolean contains(Object o)
Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).
That o.equals(e) bit is important - a data class automatically generates an equals() implementation based on its data, i.e. the properties in the constructor. So Pos(0, 0) == Pos(0, 0) is true even though they're different instances, because they contain the same data.
This is why setByCopy.contains(pos) is true - not because it contains that object, but because it contains an object that is equal to it.
When you update pos with different numbers, now pos and pos2 have different values for their data properties - they're no longer equal, so setByCopy.contains(pos) returns false.
set.contains(pos) still evaulates to true because that set contains the pos object. When you updated that object, the reference in the set is pointing to that same object, so of course it's equal to itself! If you wanted to create a distinct, separate instance that doesn't change when you update pos, then that's what copy() is for

Access CoroutineContext element via Key class

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.

Remove object from arraylist by property value

I have the following problem:
I have a arraylist called "list" which contains objects of the class "SampleClass", the objects have a property "name".
Now I would like to remove a object with a certain "name" value, without knowing the index.
I have tried the following:
list.remove(SampleClass("Village"))
So, the idea would be that the instance of SampleClass where the property name contains "Village" is removed from the list.
It compiles allright, but its not working.
If you want to remove all elements with that name, you can use removeAll:
list.removeAll { it.name == "Village" }
If you only want to remove the first item with that name:
If the name is the only property of the class, you should just make it a data class so equals() is based on the name. Then you could use the code you posted.
If it is not the only property, you will have to get the index in the list and remove it that way:
list.removeAt(list.indexOfFirst { it.name == "Village" })
The way you have it now, you would have to override the equals method in SampleClass to check if the name property is the same. Right now, it probably doesn't work because the default equals method won't compare the name property and so the SampleClass instance you want to remove with that property as "village" will be considered not equal to the SampleClass instance you're passing in.
Otherwise, you can also use list.filter { it.name != "village" }

Boolean getter is serialized twice when annotated with #JsonProperty

Suppose there is a class with a boolean property, its name starts with is:
class Preferrable {
var isPreferred: Boolean = true
}
It is serialized to {"preferred":true}, dropping is part.
As mentioned in this question, to prevent it, we need to specify property name explicitly, using #JsonProperty("isPreferred") annotation.
That approach works perfectly with Java. But in case of Kotlin class with annotated property serialized form contains property duplication: {"preferred":true,"isPreferred":true}.
The workaround is to apply annotation to property getter. It doesn't work for data classes and as for me, this code looks a bit too much for just keeping property name as is:
class Preferrable {
var isPreferred: Boolean = true
#JsonProperty(value = "isPreferred")
get() = field
}
What is the reason behind such behavior? Is it just a bug? Is there a simpler way to prevent is prefix dropping for Kotlin?
Booleans are handled a bit differently from other data types. You need to explicitly use #get in the annotation:
#get:JsonProperty("isPreferred")
var isPreferred: Boolean = true
Note that this does work with data classes, e.g.
data class Preferrable(
#get:JsonProperty("isPreferred")
var isPreferred: Boolean = true
)
See this question for a bit more info (and a link to where this is discussed in more detail).

HashMap errors - containsKey, get

Can anyone shed some light?
Problem code:
protected var table = HashMap<Class<*>, Double>()
if (table.containsKey(object)) {
value = table.get(object)
}
containsKey(K):Boolean is deprecated. Map and Key have incompatible
types. upcast to Any? if you're sure
so I changed it to this:
if (table.containsKey(object as Any?)
which fixes the error, but is this what I should have done to fix it? or is there a better way?
also .get(object) has an error:
Type inference failed. required: kotlin.Double found kotlin.Double?
same error message for this too:
val c = someObject.javaClass // pre j2k code: final Class<? extends SomeClass> c = someObject.getClass();
weight = weightingTable[c] <-- error here
I don't know what to do here
The containsKey call is reported as an error because the type of the argument you pass to it does not match the type of the map key. Your map contains classes as keys, and you're trying to pass an object instance to it. Changing this to object as Any? is not a useful fix, because this call will compile but will always return false. What you need to do instead is to use object.javaClass to get the class of the object.
The weightingTable[c] call is reported as an error because the map does not necessarily contain a value for the key you're passing to it, so the result of the [] operation is nullable. You cannot assign a nullable value to a non-null variable without somehow handling the null case (using a check, an explicit non-null cast or some other option covered in the documentation).
When doing:
myHashMap.get(object)
and getting:
Type inference failed. required: kotlin.Double found kotlin.Double?
even when you already checked with containsKey. You can solve it by using:
myHashMap.getOrElse(key) { defaultValue }