-
-
Save purplefox/5487084 to your computer and use it in GitHub Desktop.
void deployModule(String moduleName, Map<String, Object> config = [:], int instances = 1, Closure doneHandler = {}) { | |
} | |
deployModule("foo", {} ) | |
You can do pseudo named arguments by accepting a single map parameter but then of course you're responsible for any type validation.
yes, something like
deployModule(moduleName:'bla',instances:2)
It favors also best practice at naming parameters...
Hmm ok.
I would have expected Groovy to infer that I want to call the method with default values for config and instances since there is no ambiguity in this case.
Thanks for the info!
But there's another problem...
I accept the fact that default params don't work the way I like so add the following method so I can call it how I would like:
void deployModule(String moduleName, Closure doneHandler = {}) {
}
But then it barfson compile saying method with that signature is already defined. wtf?
I think this works only if doneHandler has no default arguments. If you don't do that, as you have three parameters with default arguments, I think Groovy tries to apply the closure two the first argument, which does not match.
It could probably try to guess what argument is expected. But then what "config" has type object? Should it take the argument instead of doneHandler? It probably be enhanced, but the edge cases become quickly difficult.
Too slow to answer :-D
well, yes.
But what the point of defining another function with less parameters in signature, when there is already one that default the missing parameters ?
About your last comment: this is the JVM. It wants strongly typed methods. To handle that, Groovy generates several methods for deployModule that in fact accept 1, 2, 3 or 4 arguments (you can see that in the generated bytecode or in an IDE, for instance the Overview view in Eclipse). So it collides with the methods you define later.
I think you have two options: the named arguments style, which will avoid to create several methods, or defining by hand all the method calls you want to allow.
@fix: I think he wants to allow the caller to pick what arguments they want to specify and leave others to the default.
see here http://groovyconsole.appspot.com/edit/955001
if you add default to callback, which is a better naming than doneHandler btw :), you have error compilation, but what the point?
This doesn't make a lot of sense to me. It seems there is no ambiguity here so Groovy should be able to call it.
Also, regarding generated methods, I guess Groovy generates the following:
void deployModule(String moduleName, Map<String, Object> config = [:], int instances = 1, Closure doneHandle)
void deployModule(String moduleName, Map<String, Object> config = [:], int instances = 1)
void deployModule(String moduleName, Map<String, Object> config = [:])
void deployModule(String moduleName)
None of the above should collide with:
void deployModule(String moduleName, Closure doneHandle)
So I don't see why I can't add the method.
It seems to me the only way I can fix this is to not use default arguments at all, and just overload the method multiple times like we do in Java.
But I thought the whole point of Groovy default args was to avoid having to do this - so I'm struggling to see what the point of them is.
Two methods are required:
void deployModule(String moduleName, Map config = [:], int instances = 1) {
}
void deployModule(String moduleName, Map config = [:], int instances = 1, Closure handler) {
}
Then Groovy generates these 6:
void deployModule(String moduleName) {}
void deployModule(String moduleName, Map config) {}
void deployModule(String moduleName, Map config, int instances) {}
void deployModule(String moduleName, Closure handler) {}
void deployModule(String moduleName, Map config, Closure handler) {}
void deployModule(String moduleName, Map config, int instances, Closure handler) {}
Sorry, it is colliding.
the true method generation (IMO, to be confirmed):
void deployModule(String moduleName, Map config = [:], int instances = 1, Closure doneHandle)
void deployModule(String moduleName, Map config = [:], Closure doneHandle)
void deployModule(String moduleName, Closure doneHandle)
That makes no sense to me.
Why would Groovy generate methods that all take have the doneHandler parameter in there?
This would imply I always have to specify a doneHandler, which is not the case.
doneHandler should be optional.
if you don't default parameter, it is kept in the signature, that makes sense to me
See my answer.
with the following
void deployModule(String moduleName, Map config = [:], int instances = 1, Closure doneHandle={})
it should also generates
void deployModule(String moduleName, Map config = [:], int instances = 1)
void deployModule(String moduleName, Map config = [:])
void deployModule(String moduleName)
at some point default=optional, right?
The parameter DOES have a default - see original gist
@pidster right it is, well done!
So to recap, here is the signature:
void deployModule(String moduleName, Map config = [:], int instances = 1, Closure doneHandle={})
Notice that ALL parameters have a default.
I would therefore expect Groovy to generate:
void deployModule(String moduleName, Map config, int instances, Closure doneHandler)
void deployModule(String moduleName, Map config, int instances)
void deployModule(String moduleName, Map config)
void deployModule(String moduleName)
None of which collide with
void deployModule(String moduleName, Closure doneHandler)
So I still don't see why there is a collision
It seems to me that Groovy is treating Closure parameters differently to other params?
I can see that pidsters solution does work. But it seems an ugly solution.
I can't grok why Groovy requires this workaround and can't figure it out itself.
ok right @purplefox nailed it down, sounds a bug
I think this is a design choice because there could be inconsistency with another case: the fact that two optional arguments can have the same type (for instance 2 Closure), and it would be impossible to generate the same way a signature
@pidster @purplefox @robfletcher the bottom line is that if you overload a default, you SHOULD overload all default parameters declared BEFORE in the signature.
and by the way this is the same if the arg is not a Closure
Yes, unfortunately you must provide actual parameters for all formal params up to the last one you want to override the default for.
e.g.
would work but
would not.