My current understanding of structural equality is that it compares the type and the content.
For Referential equality, it compares the address of the two objects. Also, in the case of a primitive type of var, it will print true if the content is the same.
According to the doc here --> numbers, characters and booleans can be represented as primitive values at runtime - but to the user, they look like ordinary classes.
So String should be treated as objects at runtime.
But I get true when comparing referentially two strings that have the same content.
fun main(){
val name1 = "123"
val name2 = "123"
println(name1 == name2) //true, structural equality which is same type and same content (same as equals)
// checks if left and right are same object --> referential equality
var name3 = "123"
println(name1 === name3) //true ????????
// should print false. String is basic type and not primitve
var Arr: IntArray = intArrayOf(1,2,3)
var Arr2: IntArray = intArrayOf(1,2,3)
println(Arr === Arr2) // prints false, arrays are basic type
}
Also, why doesn't equality in kotlin of both types differentiates between val and var? They are two different types of objects at their core.
Can someone point out where am I going wrong with this? I might be missing something pretty obvious here.
So as far as string referential equality check goes, I think it does the same thing as Java in that it adds everything to the string constant pool.
Here is the thread for referential string comparison in java (done by ==) -
What makes reference comparison (==) work for some strings in Java?
For Val vs Var the answer by #Tenfour04 below explains the logic.
val and var are not different types of objects. They are references, not objects at all. They have nothing to do with the behavior of the objects they are referring to. When you compare two objects, the types of variables and properties that are referencing them is not a factor whatsoever. When you call equals on one object, the runtime only uses the reference to find the object in memory. Only then is its function called.
Related
I have a MutableMap that its keys are objects from a DataClass (User dataclass), and the values are arrays from other Dataclass (Dog dataclass). If i have a variable with a User object, and i put it in the MutableMap and i test if the map contains the User, it says that is true. But after putting the user in the MutableMap if i change one of the attributes of the User object using the variable that holds the User object, the Map says that it doesnt contains the user object.
This is an example
data class User(
var name: String,
var project: String,
)
data class Dog(
var kind: String
)
fun main(args: Array<String>) {
var mapUserDogs: MutableMap<User, MutableList<Dog>> = mutableMapOf()
var userSelected = User("name2", "P2")
mapUserDogs.put(
User("name1", "P1"),
mutableListOf(Dog("R1"), Dog("R2"))
)
mapUserDogs.put(
userSelected,
mutableListOf(Dog("R21"), Dog("R31"))
)
println(userSelected)
println(mapUserDogs.keys.toString())
println(mapUserDogs.contains(userSelected))
println(mapUserDogs.values.toString())
println("\n")
userSelected.name = "Name3"
println(userSelected)
println(mapUserDogs.keys.toString())
println(mapUserDogs.contains(userSelected))
println(mapUserDogs.values.toString())
}
The prints statements show this:
User(name=name2, project=P2)
[User(name=name1, project=P1), User(name=name2, project=P2)]
true
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]
User(name=Name3, project=P2)
[User(name=name1, project=P1), User(name=Name3, project=P2)]
false
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]
Process finished with exit code 0
But it doesn't make sense. Why the map says that it doesn't contains the user object if its clear that it still holds the reference to it after being modified?
User(name=Name3, project=P2)
[User(name=name1, project=P1), User(name=Name3, project=P2)]
The user in the keys collection was also changed when i modified the userSelected variable, so now the object has it attribute name as "Name3" in both the variable and in the Map keys, but it still says that it doesnt contains it.
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?. And doing the same process in reverse shows the same. If i get from the map the user and i modify it, the userVariable is also modified but if i later test if the map contains the userVariable, it says false.
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?
There is nothing you can do that preserves both your ability to look up the entry in the map and your ability to modify the key.
Make your data class immutable (val instead of var, etc.), and when you need to change a mapping, remove the old key and put in the new key. That's really the only useful thing you can do.
To add to Louis Wasserman's correct answer:
This is simply the way that maps work in Kotlin: their contract requires that keys don't change significantly once stored. The docs for java.util.Map* spell this out:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
The safest approach is to use only immutable objects as keys. (Note that not just the object itself, but any objects it references, and so on, must all be immutable for it to be completely safe.)
You can get away with mutable keys as long as, once the key is stored in the map, you're careful never to change anything that would affect the results of calling equals() on it. (This may be appropriate if the object needs some initial set-up that can't all be done in its constructor, or to avoid having both mutable and immutable variants of a class.) But it's not easy to guarantee, and leaves potential problems for future maintenance, so full immutability is preferable.
The effects of mutating keys can be obvious or subtle. As OP noticed, mappings may appear to vanish, and maybe later reappear. But depending on the exact map implementation, it may cause further problems such as errors when fetching/adding/removing unrelated mappings, memory leaks, or even infinite loops. (“The behaviour… is not specified” means that anything can happen!)
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?
What you're trying to do there is to change the mapping. If you store a map from key K1 to value V, and you mutate the key to hold K2, then you're effectively saying “K1 no longer maps to V; instead, K2 now maps to V.”
So the correct way to do that is to remove the old mapping, and then add the new one. If the key is immutable, that's what you have to do — but even if the key is mutable, you must remove the old mapping before changing it, and then add a new mapping after changing it, so that it never changes while it's stored in the map.
(* The Kotlin library docs don't address this, unfortunately — IMHO this is one of many areas in which they're lacking, as compared to the exemplary Java docs…)
That happens because data classes in Kotlin are compared by value, unlike regular classes which are compared by reference. When you use a data class as a key, the map gets searched for a User with the same string values for the name and project fields, not for the object itself in memory.
For example:
data class User(
var name: String,
var project: String,
)
val user1 = User("Daniel", "Something Cool")
val user2 = User("Daniel", "Something Cool")
println(user1 == user2) // true
works because, even though they are different objects (and thus different references), they have the same name and project values.
However, if I were to do this:
user1.name = "Christian"
println(user1 == user2) // false
the answer would be false because they don't share the same value for all of their fields.
If I made User a standard class:
class User(
var name: String,
var project: String,
)
val user1 = User("Daniel", "Something Cool")
val user2 = User("Daniel", "Something Cool")
println(user1 == user2) // false
the answer would be false because they are different references, even though they share the same values.
For your code to work the way you want, make User a regular class instead of a data class.
That's the key difference between regular classes and data classes: a class is passed by reference, a data class is passed by value. Data classes are nothing more than collections of values with (optionally) some methods attached to them, classes are individual objects.
An API I consume returns (among other fields) a mandatory telephone1 and an optional telephone2. However, the JSON I fetch always contains both fields and a missing entry is displayed as an empty string.
{
"telephone1": "+1 555 1234",
"telephone2": ""
}
When the response is mapped to a pojo, is it preferable to translate the empty string to null? Such that:
data class(
val telephone1: String,
val telephone2: String?
}
To me, this better communicates the possible states. Should I, though? Are there drawbacks?
At the first sight, problem boils down to different checks before further data processing: x == null or x.isEmpty(). But while nullability check is generally enforced by kotlin compiler (unlike unemptiness), it seems to be a better option.
But there are still some situations when usage of null (without any compiler errors) may lead to problems in runtime (mainly related to interop with languages without enforced nullability): like implicit convertion of null to literal string "null" (when concatenating with another string), or even NPE when passed to method accepting String! (platform type) and not annotated properly.
Sticking to DDD principles, the better option would be declaration of separate datatypes:
sealed class OptionalPhoneNumber
data class PhoneNumber(val value: String) : OptionalPhoneNumber() //You may also add some checks in init block that `value` is really a phone number, not just a set of random chars
object EmptyPhoneNumber : OptionalPhoneNumber()
and defining your data class as:
data class Data (
val telephone1: PhoneNumber,
val telephone2: OptionalPhoneNumber
)
Type system will enforce you to do x is PhoneNumber checks, and thanks to smart casts it's further usage will be type-safe:
if (telephone2 is PhoneNumber) {
println(telephone2.value)
}
I have written some codes for printing out objects in array with toString()
but by using Option1 println(path.toString())
Output is [LRunningpath;#27973e9b
which is not what i want. Then i replace it with Option2 as follow
var i=0
for(i in 0 until path.size)
println(path[i].toString())
which is correct.
My questions are,
why Option 1 don't work?
what does the output in Option 1 mean?
any advice to avoid the same situation in the future?
Any hints is very appreciated. Thank you for the kindness.
my codes are as below:
fun main() {
println("Warming up")
val input1 = Runningpath("in Forest", 2000, "some houses")
val input2 = Runningpath("at lake", 1500, "a school")
val path = arrayOf(input1, input2 )
println(path.toString())
/* var i=0
for(i in 0 until path.size)
println(path[i].toString())
*/
}
class Runningpath(val name: String, val length: Int, val spot: String){
override fun toString(): String= "The Path $name ($length m) is near $spot"
}
Short answer: in most cases, it's better to use lists instead of arrays.
Arrays are mostly for historical reasons, for compatibility, and for implementing low-level data structures. In Kotlin, you sometimes need them for interoperability with Java, and for handling vararg arguments. But other than those, lists have many advantages.
The problem is that on the JVM, an array is very different from all other objects. It has only the methods inherited from Object, and doesn't override those. (And you can't create your own subclasses to override or add to them.)
In particular, it has the toString() method from Object. That gives a code indicating the type — here [ for an array, L indicating that each element is a reference, Runningpath giving the type of reference, ; and # separators, and a hex representation of the array's hash code, which may be its address in memory or some other unique number.
So if you want some other way of displaying an array, you'll have to do it ‘manually’.
Other problems with arrays on the JVM result from them having run-time typing — they were part of Java long before generics were added, and interoperate badly with generics (e.g. you can't create an array of a generic type) — and being both mutable and covariant (and hence not type-safe in some cases).
Lists, like other Collections and data structures, are proper objects: they have methods such as toString(), which you can override; they can have generic type parameters; they're type-safe; they can have many implementations, including subclasses; and they're much better supported by the standard library and by many third-party libraries too.
So unless you have a particular need (vararg processing, Java interoperability, or a dire need to save every possible byte of memory), life will go easier if you use lists instead of arrays!
You can use the joinToString for that:
println(path.joinToString("\n"))
The joinToString() is actually available for both the List and the Array, but I'd recommend using the List as you'd have immutability and many other extensions on that, that will help your on manipulating the datas.
We are encouraged to have immutable variables as much as we can.
But sometimes when I have to modify a list I start to wonder which approach would be better for current situation...
val mutableList = mutableListOf()
// where I can just .add() / .remove() accordingly
or
var immutableList = listOf()
// where I can create a new list (using filter or `+`) each time a change is made
I guess there are different scenarios one is preferred over the other.
Hence I would like to know when one should be used over the other
val -> You could think that you can't reassign for the variable.
//that is ok
var a:Int = 1
a=2
//Even you can reassign but you can't change its type
a= "string" //that's wrong
//that is wrong
val b:Int = 1
b = 2
ListOf -> You could think that you can't insert/delete/alter any element in the list
(can't do anything to the content of the list)
var list:List<Int> = listOf(1,2,3,4) //[1,2,3,4]
//you can read list
list.get(0)
list[0]
//but you can't change(/write) the content of the list (insert/delete/alter)
list.set(0, 100)
list.add(5)
list.removeAt(0)
var mutableList:MutableList<Int> = mutableListOf(1,2,3,4) //[1,2,3,4]
//you can read and write
mutableList.get(0)
mutableList.set(0, 100) //[100,2,3,4]
mutableList.add(5) //[100,2,3,4,5]
mutableList.removeAt(0) //[2,3,4,5]
SO
combine both of them, you will get four cases
Case 1: var mutableList:MutableList = mutableListOf(1,2,3,4)
//you can reassign
mutableList = mutableListOf(4,5,6,7) //[4,5,6,7]
//you can alter the content
mutableList.set(0, 100) //[100,5,6,7]
mutableList.add(8) //[100,5,6,7,8]
mutableList.removeAt(0) //[5,6,7,8]
Case 2: val mutableList:MutableList = mutableListOf(1,2,3,4)
//you can't reassign
mutableList = mutableListOf(4,5,6,7) //that's wrong
//you can alter the content
mutableList.set(0, 100) //[100,2,3,4]
mutableList.add(8) //[100,2,3,4,8]
mutableList.removeAt(0) //[2,3,4,8]
Case 3: var list:List = ListOf(1,2,3,4)
//you can reassign
list= ListOf(4,5,6,7) //[4,5,6,7]
//you can't alter the content
list.set(0, 100) //that's wrong
list.add(8) //that's wrong
list.removeAt(0) //that's wrong
Case 4: val list:List = ListOf(1,2,3,4)
//you can't reassign
list= ListOf(4,5,6,7) //that's wrong
//you can't alter the content
list.set(0, 100) //that's wrong
list.add(8) //that's wrong
list.removeAt(0) //that's wrong
//the only thing you can do is Read
list.get(0) //return 1
list[0] //return 1
Mutable and immutable list increase the design clarity of the model.
This is to force developer to think and clarify the purpose of collection.
If the collection will change as part of design, use mutable collection
If model is meant only for viewing, use immutable list
Purpose of val and var is different from immutable and mutable list.
val and var keyword talk about the how a value/reference of a variable should be treated.
var - value/reference assigned to a variable can be changed at any point of time.
val - value/reference can be assigned only once to a variable and can't be changed later point in the execution.
It is completely valid in Kotlin to assign mutable list to a val and add element to it.
val a = mutableListOf(1,2,3,4)
a.add(5)
println(a)
will output
[1, 2, 3, 4, 5]
I guess there are different scenarios one is preferred over the other. Hence would like to get to know when one should be used over the other etc.
There are several reasons why immutable objects are often preferable:
They encourage functional programming, where state is not mutated, but passed on to the next function which creates a new state based on it. This is very well visible in the Kotlin collection methods such as map, filter, reduce, etc.
A program without side effects is often easier to understand and debug (you can be sure that the value of an object will always be the one at its definition).
In multithreaded programs, immutable resources cannot cause race conditions, as no write access is involved.
You have also some disadvantages:
Copying entire collections just to add/remove a single element is computationally expensive.
In some cases, immutability can make the code more complex, when you tediously need to change single fields. In Kotlin, data classes come with a built-in copy() method where you can copy an instance, while providing new values for only some of the fields.
Which one you end up using depends on the use case at hand. For data classes (bundling a few attributes together), it's often a good idea to stick to immutability. For collections, if you use immutable ones just to modify their copies and re-assign the reference pointing to them all the time, you can as well use mutable ones. If you share a collection to many parts of your application that depend on the state remaining constant, use immutable.
Keep in mind that Kotlin collections have different concepts:
Mutable collections: MutableList<T>, MutableSet<T>, MutableMap<T>
These can be modified at any time.
Read-only collections: List<T>, Set<T>, Map<T>
These provide a read-only view on the collection, i.e. the collection cannot be modified through that reference. It gives no guarantee about immutability though (another mutable reference to it can still exist and used for modification).
(Proposed, not yet part of Kotlin)
Immutable collections: ImmutableList<T>, ImmutableSet<T>, ImmutableMap<T>
These would guarantee true immutability, and provide patterns to build new modified collections based on them. See the Proposal for details.
What is the difference between referential equality and structural equality
in Kotlin?
val a = File("/myfile.txt")
val b = File("/myfile.txt")
val sameRef = a === b
and:
val a = File("/myfile.txt")
val b = File("/myfile.txt")
val both= a == b
Referential equality === (also called identity) means that the pointers for two objects are the same. That is to say the objects are contained in the same memory location which leads us to the fact that pointers reference to the same object.
identity: determines whether two objects share the same memory address
Structural equality ==, in its turn, means that two objects have equivalent content. You should specify when two objects should be considered equal by overriding the equals() method.
equality: determines if two object contain the same state.
As well as in Java, in Kotlin there're no specific equals() and hashCode() generated by default (not considering data classes). Thus, until you've overriden these methods for your class, both == and === perform identity comparison.