groovyargumentsargument-passing

Groovy named and default arguments


Groovy supports both default, and named arguments. I just dont see them working together.

I need some classes to support construction using simple non named arguments, and using named arguments like below:

def a1 = new A(2)
def a2 = new A(a: 200, b: "non default")

class A extends SomeBase {
    def props
    A(a=1, b="str") { 
        _init(a, b)
    }

    A(args) { 
       // use the values in the args map:
       _init(args.a, args.b)
       props = args
    }

    private _init(a, b) {
    }

}

Is it generally good practice to support both at the same time? Is the above code the only way to it?


Solution

  • The given code will cause some problems. In particular, it'll generate two constructors with a single Object parameter. The first constructor generates bytecode equivalent to:

    A() // a,b both default
    A(Object) // a set, b default
    A(Object, Object) // pass in both
    

    The second generates this:

    A(Object) // accepts any object
    

    You can get around this problem by adding some types. Even though groovy has dynamic typing, the type declarations in methods and constructors still matter. For example:

    A(int a = 1, String b = "str") { ... }
    A(Map args) { ... }
    

    As for good practices, I'd simply use one of the groovy.transform.Canonical or groovy.transform.TupleConstructor annotations. They will provide correct property map and positional parameter constructors automatically. TupleConstructor provides the constructors only, Canonical applies some other best practices with regards to equals, hashCode, and toString.