Search code examples
listgroovygetter-setter

Groovy - Appending to list available only via getter-setter


In a Groovy class I want to expose internal String property as a List through getters and setters. I want the property to behave just like a regular list property to which I can add element using obj.list << newElement. However it does not work and I have to use work-around obj.list = obj.list << newElement.

Is there any other way to do the trick in Groovy? Any method to be implemented in the class, that will be called when << operator is used?

Code example:

class Test {

    String internal = 'a,b,c'

    List getList() {
        return internal .split(',')
    }

    void setList(List list) {
        internal = list.join(',')
    }

}

def t = new Test()
println t.internal          // a,b,c
println t.list              // [a, b, c]
t.list << 'd'               // this does not work! does not add new element
println t.list              // [a, b, c] 
t.list = t.list << 'd'      // work-around that works
println t.list​​              // [a, b, c, d]

Solution

  • Consider the following:

    class Test {
        String internal = 'a,b,c'
    
        List getList() {
            def list = internal.split(',') as List
    
            // this will override so:
            // << 'd'       becomes appendToList(['d'])
            // << ['e','f'] becomes appendToList(['e','f'])
            list.metaClass.leftShift = { def x ->
                this.appendToList([x].flatten())
            } 
    
            return list
        }
    
        void appendToList(List list) {
            internal = internal + "," + list.join(',')
        }
    
        void setList(List list) {
            internal = list.join(',')
        }
    }
    

    Note it handles both cases of (a) one item (b) a list of items

    def t = new Test()
    t.list << 'd'
    assert ['a','b','c','d'] == t.list
    t.list << ['e','f']
    assert ['a','b','c','d','e','f'] == t.list
    t.list = ['x','y']
    assert ['x', 'y'] == t.list