Your own methods
Naming a piece of work: parameters, default arguments that fill in what the caller leaves out, and the last expression that answers for you.
A name for a piece of work
A method looks like a method anywhere else: def greet(name) { ... } declares it, greet('Ada') calls it. def in front means "the return type is whatever comes back"; you can also write the type — String greet(name) — and then the answer is checked against it.
The body's last expression is the return value. That is the same rule closures follow, and it is why the shortest methods are one line of expression with no return at all. return still works, and is what you want when there is an early exit in the middle of the body. A written return type is applied on the way out, so a method declared String hands back the text form of whatever its last expression produced, and fails there if no conversion exists.
Defaults, so the common case stays short
A parameter can carry a default: def order(item, quantity = 1, unitPrice = 5). A caller who gives one argument gets the defaults for the rest, and a caller who needs the others passes them — order('pen', 3, 2).
Defaults are read left to right, so the ones with defaults belong at the end; a required parameter after a defaulted one makes the call ambiguous. A useful trick for lists: def total(numbers = []) lets the method be called with nothing at all, and the empty list keeps the body free of null checks. order("pen", 3) fills quantity and leaves unitPrice at its default, and there is no way to skip the middle argument and supply the last one by position.
One job, one name
A method earns its keep by being callable twice without editing the caller. If the body is longer than a screenful, or the name needs an "and" to describe it, that is usually two methods — and the second one will be needed by another caller soon anyway.
Methods and closures are not rivals: a closure is a value you pass around, a method is a name on the script or class. When a block is long or used in three places, promoting it to a method with a name improves every place at once.
Parameters, defaults and answers
def area(width, height = width) {
width * height
}
println area(3)
println area(3, 4)
def shout(text, times = 1) {
(text.toUpperCase() + '!') * times
}
println shout('hello')
println shout('hello', 3)
def classify(score) {
if (score >= 90) return 'A'
if (score >= 70) return 'B'
'C'
}
println classify(95)
println classify(71)
println classify(10)How a call finds its method
Groovy resolves method calls at run time. The script is compiled to a class, and a call carries its name and arguments; the runtime picks the method whose parameter types fit the values. That is why a method can be defined below the line that calls it, and why an argument of the wrong type fails at the call rather than at the definition.
Overloading uses the same machinery: two methods with one name and different parameters are distinct, and the runtime picks by the actual values. It also means a typo in a method name is not caught until that line runs, so the error names the call rather than the definition. The symptom is a MissingMethodException at the call, with the name and the argument types printed; read those two before the line number.
Script methods and script variables
A method defined at the top level of a script belongs to the generated script class. A def variable defined at the top level belongs to the generated run() method. They are different scopes, so the method cannot see the variable, and Groovy reports it as missing rather than capturing it.
The fix is a parameter. def greet(name) and greet(who) says exactly what the method needs and keeps it usable from another script. A value that changes should be passed in; this is also why methods are easier to test than script bodies.
Three method mistakes: a top-level def variable is invisible inside a method, so pass it as an argument; a defaulted parameter placed before a required one makes the required argument unreachable, so defaults go last; and a println as the last line makes the method answer null, because println answers nothing.
Try these next
- Write
def repeat(text, times = 2)and call it with one argument and with two. - Add an early
returnfor an invalid input and check that the rest of the body is skipped. - Move a script variable into a parameter and confirm the method still prints the same line.
A greeting and an order form
Run it and count the calls. Then add a method of your own with two parameters, one of them defaulted, and call it twice — once with the default and once without.
A tip calculator with a guard
Run it and check the split by hand. Then call `bill` with a rate of `0.2`, and add a method that rounds a value to a whole number before returning it.
A method defined in a script is visible to every statement in that script, including statements written above it. The script is compiled as a whole before it runs, so the order in the file does not matter.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does def square(n) { n * n } return?
