❯ Guillaume Laforge

Groovy default params to avoid one-argument methods being called without params

Recently, I saw an interesting tweet, mentioning a JavaScript trick using default parameters to make a parameter mandatory.

In a language like Apache Groovy, when statically compiled, you’d get a compilation error if you forgot a parameter, because the signature couldn’t be found by the compiler. In dynamic mode, you’d get a runtime error though, with a MissingMethodException (and the error message should give you a hint as to which method you should actually call instead).

But there’s a particular case of the Groovy method dispatch that’s a bit special (and actually something we might be removing at some point in a breaking version of the language, but it’s been there since 1.0). When you have single-argument methods, you’re allowed to call those methods without passing a parameter! And the parameter is filled simply with null. So you might have made a mistake in your code, forgetting to pass an actual parameter value, but you’d get neither a compilation error nor a runtime exception, but a null value.

So I thought about that JavaScript trick, and adapted to this situation, to ensure that you can’t call a one-argument method without an argument.

String up(String s) {
    s?.toUpperCase()
}

assert up('groovy') == 'GROOVY'

// a strange aspect of Groovy is that 
// you can call a one-argument method without passing any actual argument
// as if you were passing null, as in up(null)

assert up() == null

// let's use the JavaScript trick with mandatory default params:
// https://twitter.com/djsmith42/status/679018096334675968

String up2(String s = mandatory('s')) {
    s?.toUpperCase()
}

void mandatory(String paramName) {
    throw new Exception("Please provide an actual value for '$paramName'")
}

assert up2('groovy') == 'GROOVY'

try {
    up2()
} catch(emAll) {
    assert emAll.message == "Please provide an actual value for 's'"
}

// another approach with using a closure call as default value

String up3(String s = { -> throw new Exception("Please provide an actual value for 's'") }() ) {
    s?.toUpperCase()
}

assert up3('groovy') == 'GROOVY'

try {
    up3()
} catch(emAll) {
    assert emAll.message == "Please provide an actual value for 's'"
}

I’ve also pushed that example on the Groovy Web Console, if you wanna play with it.