5

Calling the add() method on a list doesn't force the setter to be called.

First off, why is the setter not running? Secondly, is there a way to somehow override List.add() and steal the action, or do I need a custom Iterable?

Sample code:

system.debug('adding');
mylist.add(1);
system.debug('added');

public integer[] mylist
{
    get
    {
        if (mylist == null) mylist = new integer[]{};
        system.debug('get');
        return mylist;
    }
    set
    {
        system.debug('set');
        mylist = value;
    }
}

Debug logs:

DEBUG|adding
DEBUG|get
DEBUG|added
DEBUG|set

I am expecting the setter to run after the "adding" statement, but it's the getter that runs.

8
  • Can you please add code snippet. – Ilya Lepesh Jul 12 '16 at 18:53
  • We will need more details to be able to help answer your question. Without more details (like including the code that's not working as you expect), I'm having a hard time figuring out why you think calling <List>.add() would have any effect other than simply adding something to a list. – Derek F Jul 12 '16 at 18:55
  • Calling List.add() is setting the List -am I wrong? I thought it was obvious but anyway I have added sample code to demonstrate the problem. – Mossi Jul 12 '16 at 19:03
  • 1
    Actually add should not call the setter. You can add many times to a final list... – Adrian Larson Jul 12 '16 at 20:05
  • 1
    @Mossi So, really, this ended up being an X-Y Problem. No worries. Why don't you try asking that question, basically phrased in your last comment, what you've tried so far, and let us help you find a solution. – sfdcfox Jul 12 '16 at 22:09
4

In order to understand what's happening, you need to mentally separate the variable from the object it contains.

Initially, when you declare a variable, the variable is empty ("null"); this is an area of space that's traditionally on the "stack" and allocated at compile-time.

mylist -> mylist contains null

Next, when you create a new object, the runtime provides an empty space to store data:

new List<Integer>(); // Creates an object at address 123456

Then, when the value is assigned, the variable's setter is called:

mylist.set(Object<123456>) -> mylist contains Object<123456>

By default, it just assigns the value to that reference, but in this case, you've overridden it to call your custom method (set).

After that, when you want to use the object in the variable, it has to call "get", which returns the object:

(mylist.get() returns Object<123456>).add(Object<654321>)

This is why getters are called when you try to call a function on a variable.

Those things we call "variables" are allocated on the "stack" and do not consume memory. All variables have a get and set function, which can be overridden. The stack is of a limited, undocumented size. The stack also does other stuff, like remember which function to return to when the current function ends.

The objects that we create through "new" (either explicitly or implicitly) are stored in the "heap." This is the area that is limited to 6/12MB. When we have a non-null value in a variable, it is a reference to somewhere within this "heap" area. The underlying architecture is actually more complicated than that, but that's all you really to know about why getters and setters work they way they do.


You can't override any function in the standard library. They're all "final", meaning that they are read-only and cannot be redefined or extended.

You also don't ordinarily need to write an iterator for the List and Set objects, because they have a built-in one:

Iterator<Integer> iter = mylist.iterator();
while(iter.hasNext()) {
    Integer currentValue = iter.next();
}
5
  • This is all good. But adding an element to a list in memory should set the list, right? Shouldn't it be overwriting the address in memory? I understand why the getter is called, but I still don't get the reason for not calling the setter. By custom iterator I meant to create a separate class that implemented the Iterable interface and replaced my list with it. – Mossi Jul 12 '16 at 22:27
  • @Mossi The setter is not called because the variable is not being modified-- the object is. The only way to modify a variable is by way of the = assignment operator. That's the only thing that can trigger a setter, and it must be done outside of a setter and getter, to avoid infinite loops. Any method you call on an object does not affect the variable that references the object. – sfdcfox Jul 12 '16 at 22:41
  • @Mossi Depending on the exact nature of the two classes, you might be able to simply assign the value in the child class directly to the abstract class's list. There's no rule saying that an Object can only be referenced by a single variable. That was the reasoning behind suggesting a new question-- I believe there's an easier way than going through the hassle of a custom iterator. – sfdcfox Jul 12 '16 at 22:45
  • That is exactly how I'm working around it. Every time a new element is added to the child list I set baseList = childList. Also I understand your point about the object being modified, and I guess any further conversation warrants a separate discussion (probably in a Java forum). – Mossi Jul 13 '16 at 0:30
  • @Mossi You only need to set the baseList once; since the two variables will point to the same object (same place in memory), the two will remain in sync. – sfdcfox Jul 13 '16 at 2:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.