This code works as expected (output is j.doe
):
class Sample {
Closure formatFirstName
Closure formatLastName
void doStuff (String fname, String lname, Closure callback) {
formatFirstName(fname) { String fnameError, String formattedFname ->
formatLastName(lname) { String lnameError, String formattedLname ->
String errors = "${fnameError ? "${fnameError}, " : ''}${lnameError ?: ''}"
callback(errors, formattedFname, formattedLname)
}
}
}
}
Closure ffn = { String fname, Closure callback ->
String firstInitial = fname?.substring(0,1)
if (!firstInitial)
callback('invalid first name', null)
else
callback(null, firstInitial.toLowerCase())
}
Closure fln = { String lname, Closure callback ->
String lastPrefix = lname?.size() > 2 ? lname.substring(0,3) : null
if (!lastPrefix)
callback('invalid last name', null)
else
callback(null, lastPrefix.toLowerCase())
}
Sample sample = new Sample(formatFirstName: ffn, formatLastName: fln)
sample.doStuff('John', 'Doe') { String errors, String formattedFname, String formattedLname ->
if (errors)
println errors
else
println "${formattedFname}.${formattedLname}"
}
Adding the @CompileStatic
annotation causes an exception:
groovy.lang.MissingMethodException: No signature of method: Sample$_doStuff_closure1.doCall() is applicable for argument types: (java.lang.String, Sample$_doStuff_closure1_closure2) values: [Doe, Sample$_doStuff_closure1_closure2@bb70963]
Possible solutions: doCall(java.lang.String, java.lang.String), findAll(), findAll()
at Sample$_doStuff_closure1.doCall(ConsoleScript226:11)
at ConsoleScript226$_run_closure1.doCall(ConsoleScript226:26)
at Sample.doStuff(ConsoleScript226:10)
at Sample$doStuff.call(Unknown Source)
at ConsoleScript226.run(ConsoleScript226:40)
(Code with @CompileStatic
annotation)
import groovy.transform.CompileStatic
@CompileStatic
class Sample {
Closure formatFirstName
Closure formatLastName
void doStuff (String fname, String lname, Closure callback) {
formatFirstName(fname) { String fnameError, String formattedFname ->
formatLastName(lname) { String lnameError, String formattedLname ->
String errors = "${fnameError ? "${fnameError}, " : ''}${lnameError ?: ''}"
callback(errors, formattedFname, formattedLname)
}
}
}
}
Closure ffn = { String fname, Closure callback ->
String firstInitial = fname?.substring(0,1)
if (!firstInitial)
callback('invalid first name', null)
else
callback(null, firstInitial.toLowerCase())
}
Closure fln = { String lname, Closure callback ->
String lastPrefix = lname?.size() > 2 ? lname.substring(0,3) : null
if (!lastPrefix)
callback('invalid last name', null)
else
callback(null, lastPrefix.toLowerCase())
}
Sample sample = new Sample(formatFirstName: ffn, formatLastName: fln)
sample.doStuff('John', 'Doe') { String errors, String formattedFname, String formattedLname ->
if (errors)
println errors
else
println "${formattedFname}.${formattedLname}"
}
Using explicit invocation (i.e. closure.call
) this now works again:
import groovy.transform.CompileStatic
@CompileStatic
class Sample {
Closure formatFirstName
Closure formatLastName
void doStuff (String fname, String lname, Closure callback) {
formatFirstName.call(fname) { String fnameError, String formattedFname ->
formatLastName.call(lname) { String lnameError, String formattedLname ->
String errors = "${fnameError ? "${fnameError}, " : ''}${lnameError ?: ''}"
callback(errors, formattedFname, formattedLname)
}
}
}
}
Closure ffn = { String fname, Closure callback ->
String firstInitial = fname?.substring(0,1)
if (!firstInitial)
callback('invalid first name', null)
else
callback(null, firstInitial.toLowerCase())
}
Closure fln = { String lname, Closure callback ->
String lastPrefix = lname?.size() > 2 ? lname.substring(0,3) : null
if (!lastPrefix)
callback('invalid last name', null)
else
callback(null, lastPrefix.toLowerCase())
}
Sample sample = new Sample(formatFirstName: ffn, formatLastName: fln)
sample.doStuff('John', 'Doe') { String errors, String formattedFname, String formattedLname ->
if (errors)
println errors
else
println "${formattedFname}.${formattedLname}"
}
So my question is: why do I have to use explicit closure invocation for member closures when using compile static? Is it simply because Java doesn't have implicit invocation? If this was the case wouldn't we have to explicitly invoke the callback
closure being passed to the doStuff
method as well?