We’re updating the Apex documentation regarding the concept of “pass by reference.” Here’s an in-depth explanation of the reasons behind this modification.
We’re updating the Apex documentation regarding the concept of “pass by reference.” Here’s a comprehensive explanation detailing the rationale behind this modification.
The current Apex documentation suggests that non-primitive variables are passed by reference:
“Also note that all primitive variables are passed by value, while all non-primitive data types are passed by reference.”
However, strictly speaking, this statement is inaccurate. The revised text will state:
“In Apex, all primitive data type arguments, such as Integer or String, are passed into methods by value. Consequently, any alterations to these arguments exist solely within the method’s scope and are lost once the method returns.
Similarly, non-primitive data type arguments, such as sObjects, are also passed into methods by value. After the method’s execution, the passed-in argument still references the same object as before the method call and cannot be altered to point to another object. Nevertheless, modifications to the object’s field values can be made within the method.”
The behavior often mimics pass-by-reference for non-primitives, which might obscure the fact that they are not actually passed by reference. However, when manipulating the variable directly within the method—such as calling “new” on it—differences from pass-by-reference become noticeable.
We’ll demonstrate this through code samples. But before that, let’s take a step back and define these terms.
“Pass by value” refers to a method creating a duplicate copy of the parameter variable in memory when called, and this copy is passed as a parameter. Changes made to this copy in the method do not affect the original variable in the caller, and alterations to the copy are discarded upon return.
“Pass by reference” indicates that the actual variable is passed to the method.
As explained in the revised developer’s guide text, Apex does not pass anything by reference. It passes the reference (i.e., the memory address) by value, allowing developers to modify object fields and invoke methods (like list.add()) on the object.
Let’s demonstrate this with some code. First, an example showcasing apparent pass-by-reference behavior:
List fillMe = new List(); PassByWhatNow.reference(fillMe); System.assertEquals(fillMe.size(),5); //five items, as expected /* calls this method public static void reference(List m) { List la = [select id, name from Account limit 5]; for (Account a : la) m.add(a); } */
In this instance, the function’s parameter, representing the fillMe collection, is essentially a pointer passed by value. In simpler terms, the memory address is copied, preserving the same memory address number in both instances. Upon invoking the add() function on this copy, the alterations affect the very same memory location by appending the specified items to the list. Consequently, upon returning to the calling program, the modifications made in the static function remain visible. Everything appears to be functioning as anticipated.
However, let’s delve into an example where this behavior fails.
List createMe = new List(); PassByWhatNow.referenceNew(createMe); System.assertEquals(createMe.size(),0); //nothing in the list /* calls this method public static void referenceNew(List m) { m = new List([select id, name from Account limit 5]); } */
Once again, we pass a duplicate of the list’s memory address to the function. However, this time, the function initializes the variable anew. Upon invoking the “new” function, fresh memory allocation occurs—an essential distinction.
The copy of the memory address passed as a parameter gets overridden with the address of the newly allocated memory. Nevertheless, the original memory address remains unaltered. This alteration in address remains unknown to the caller!
Subsequent operations within the method occur on this new memory address rather than the original object address. These modifications, including the addition of Accounts to the list, remain inaccessible to the caller, as it is only aware of the original address. Given that no actions were performed on this address, the list within the caller remains unaltered and still empty, just as it was during the initial call.
Although Apex typically behaves akin to passing variables by reference, it doesn’t truly employ pass-by-reference. In certain scenarios, this distinction becomes crucial, as elucidated here. Understanding these nuances empowers you as a developer with deeper insights into the language’s behavior.