Yesterday I was having a discussion with one of my colleague over parameter passing techniques in apex programming. He was pretty sure that primitives in apex are passed by values, whereas non-primitive data types are passed by reference. Well, this statement is partially correct.
Definitely, primitive data types are passed by value in apex and I'm sure most of you would agree. But, are non-primitives really passed by reference? The answer is No. Even non-primitives are passed by value in apex programming. Surprised? Let me walk you through the below examples to understand it better.
Reason for this misconception
First, let us see why many developers have the misconception that non-primitives are passed as reference in apex.
Consider below function that takes in a parameter of type Account and updates name of the account to 'Test 2'.
Now, when we execute below three lines of code, the output is 'Test 2' i.e. updated value of the account name.
Below figure illustrate this behaviour
This makes us think that the parameter is passed as reference because we were able to retrieve the updated account name outside the function. But this is not what actually happens behind the scenes.
Behind the scenes of parameter passing
In apex, even the non-primitives are passed as value (value of a reference is passed instead of the object itself). To make it more clear, now let us consider below method which too update the account name but only after creating a new instance of account.
Now, if we re-execute the same three lines of code as in the previous example, the output this time will be 'Test' i.e. the old value of the account and not the updated one. Wow, that's something unexpected. Had the parameter been passed as reference, the output would have been 'Test 2'. Let me explain each line of code out here in detail.
Statement 1 simply creates a variable named var1 and assigns a new instance of account with a name as 'Test'. Here var1 stores the start address of instance of account, say 4032. In statement 2, we pass var1 as parameter to updateAccountName method. Here the value of reference variable var1 is passed, i.e. 4032 is passed and not the object itself and is copied on local parameter of method named var2. At this point of time, both var1 and var2 stores the value as 4032 (start address of account instance) as shown in below figure.
In statement 4, var2 is assigned a new instance of account i.e. new address value at which new instance of account is stored, say 8058. At this point of time, var1 holds value as 4032 whereas var2 holds value as 8058. In statement 5, the name of a second instance of account is updated to 'Test 2'. At this point, memory assignment is as illustrated in below figure.
In statement 3, var1 references the account name stored at 4032 and prints it to debug log. This makes it clear that the updated value of var2 was not available outside the method call, just like in parameters passed as value.
To summarize, just keep in mind, when passing primitive data types, actual values are passed as parameter, whereas when passing non-primitive data types, value of the reference is passed.