Levi
Levi

Reputation: 43

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 :)

Upvotes: 0

Views: 839

Answers (2)

Tenfour04
Tenfour04

Reputation: 93834

You compared changing the contents of a list with changing the value of a variable, which are not the same thing. In your example, mPi is first assigned to point at the value of Data.pi and then it’s changed to point at some other Double. You aren’t doing anything to modify the Double itself, only what the local variable mPi is pointing to.

When you do val newList = Data.list, you are declaring the variable to point to the same list instance as in Data. Then you modify the contents of that same list. Since both variables are pointing at the same instance, you will observe the same changes if you inspect the instance via either variable. Notice you did not reassign the variable’s value using newList = like you did in the pi example, which is why these are not analogous actions.

In short, with the pi example, you have two Double instances and were switching between them by changing the value of a variable. With the list example, you have only one MutableList instance that you are internally mutating.

Notably, primitive type classes are all immutable so there’s no way you could modify their contents even if you wanted to. They don’t have functions you can call on them to change the value they represent. In Kotlin you rarely have to think about the distinction between primitive classes and other classes unless you’re optimizing code performance.

Upvotes: 1

Bilal Naeem
Bilal Naeem

Reputation: 1087

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.

Upvotes: 2

Related Questions