javaandroidkotlinmutablelist

Kotlin Why Changing Local var also updating companion object's var


If I clear() my local list (mList) it also clearing companion's Mutablelist (list) Why this happening explanation's are welcome :)

I've a class with companion like this :

class Data {
    companion object {
        var list:MutableList<String> = ArrayList()
    }
}

If I create my local list something like this :

fun main() {
   // some dummy data
   Data.list.add("Lion") 
   Data.list.add("Cat")
   Data.list.add("Dog")
   Data.list.add("Cheetah")

   // my local mList
   val mList = Data.list

   println("Before Clearing : mList = $mList\n list = ${Data.list}")
   mList.clear()
   println("After Clearing : mList = $mList\n list = ${Data.list}")
}

OutPut

Before Clearing : mList = [Lion, Cat, Dog, Cheetah]
 list = [Lion, Cat, Dog, Cheetah]
After Clearing : mList = []
 list = []

As You can see in output if I clear() local mList it is clearing companion's list Why it is ?

if I do this same with some other things like double it is not happening like this example -

// same Data class's
...
  var pi = 3.14
...

If change local mPi it doesn't change pi :

var mPi = Data.pi
println("Before Assigning to New value mPi =  $mPi and pi = ${Data.pi}") 
mPi = 319.12
println("After Assigning to New value mPi =  $mPi and pi = ${Data.pi}") 

2nd Output

Before Assigning to New value mPi =  3.14 and pi = 3.14
After Assigning to New value mPi =  319.12 and pi = 3.14

Link of Kotlin Playground

Why it is happening? I'd like to know :)


Solution

  • In order to understand what is going on here you need to know how values are saved in memory. All objects that are created using the new keyword (not needed in Kotlin), are stored in heap memory whereas all primitive data types are in stack memory.

    Consider the following code

    val list: MutableList<String> = ArrayList()
    

    What is happening here is that a new object of ArrayList is being created in the heap. The variable list can be considered as a box which holds the location where the actual object is created. You can imagine the variable holding a value of 0x7800abd12.

    Now what happens, when you create a new variable and assign the one that you created above.

    val newList = list
    

    Here, you assigned whatever value the variable list had to the variable newList. What this did was in fact save the memory address 0x7800abd12 in the variable newList. Remember, the value is the location where the actual list is created. Now, if the user accesses either list or newList, they will be taken to the object at the given memory address. Since both variables have the same address, any change made to either of the two variables will reflect for both of them.

    But why doesn't this behaviour reflect when we are using primitive data types. Remember, I mentioned that primitive data types are stored on the stack rather than heap. Consider this example

    val pi = 3.142
    

    In this case, the variable pi actually holds the value 3.142 rather than a memory location. Now, when you assign it to another variable, the new variable also holds the value rather than a memory location.

    val newPi = pi
    

    Here newPi holds the value 3.142. If you change newPi to some other value, it will only update the variable newPi and won't update the variable pi.

    In order to learn more about this you can check out this thread.