I had a method that have a closure as one of its parameters and I wished to cast the input closure's param with the #ClosureParam annotation:
def <T> List<T> randomInstances( final int size, final Builder<T> builder,
#ClosureParams( SecondParam.FirstGenericType )
final Closure<Void> postProcessor = null ) {
( 0..<size ).collect {
def instance = builder.build()
if ( postProcessor ) {
postProcessor( instance )
}
instance
}
}
Now, I've added the second method that will do the same, but the closure will receive two params:
def <T> List<T> randomInstances( final List<?> listToIterate,
final Builder<T> builder,
#ClosureParams( FirstParam.FirstGenericType )
#ClosureParams( SecondParam.FirstGenericType )
final Closure<Void> postProcessor = null )
I'm pretty sure that two #ClosureParams in line it's wrong use case. But I've not found the way how to pass two closure's parameters "declaration" to the method signature.
Is it possible?? Could anybody help??
P.S. When I used the annotation, I had expect not only describing for future reader, but also to help IDEA to infer the param's type. But I didn't get that result?? What I do wrong, or IDEA just don't support this feature??
In this case you can use groovy.transform.stc.FromString with ["T,U", "T"] options:
#ClosureParams(value = groovy.transform.stc.FromString, options = ["T,U", "T"])
And here is a short example:
class Lists {
static <T,U> List<T> randomInstances(List<U> listToIterate, final Builder<T> builder, #ClosureParams(value = FromString, options = ["T,U", "T"]) final Closure<T> postProcessor = null) {
(0..<listToIterate.size()).collect {
def instance = builder.build()
if (postProcessor) {
postProcessor(instance)
}
instance
}
}
}
And here is what what IDE param suggestion looks like for given method definition:
Related
I am experimenting with some dynamic variable creation with GroovyShell and encountered an issue. First, the working code:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
This gives me the expected output:
--- hello ---
However, I want to call setVariable() dynamically when getVariable() is called, something like this:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
But this fails with the below error:
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
at Script1.run(Script1.groovy:1)
at Script1$run.call(Unknown Source)
at BindingTest.run(BindingTest.groovy:23)
When I added tracing code like this:
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
In the working case, I get the below output:
setVariable: test: Test
--- hello ---
In the non-working case, I get this output:
setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
Two questions:
In the working scenario, why is there no getVariable?
In the non-working scenario, why is the Test object returned by getVariable getting rejected?
Note that this issue is specific to callable values. If I set a simple value such as a string, to test, then both approaches work fine. E.g., with this sort of a change:
...
static def defVar(def glob) {
glob.setVariable('test', '--- hello ---')
}
...
shell.parse('println(test)').run()
I get the below identical output with both approaches:
setVariable: test: java.lang.String
getVariable: test: java.lang.String
setVariable: test: java.lang.String
--- hello ---
Though, I am not sure why setVariable gets called twice. I couldn't find any documentation explaining these puzzling behaviors. Could anybody here shed some light on them?
Please note, all the code snippets have been simplified for the ease of demonstrating the problem rather than for their intended purpose
When you use a property as a callable fallback, the Binding.getVariable() method does not get involved. This behavior is controlled by the metaclass, and in your case, it all drives to the execution of the MetaClassImpl.invokePropertyOrMissing() method. This method determines if
test()
should invoke test.call() (in case of an existing property), or should it fallback to the missingMethod() method. Here is what this method implementation looks like:
private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
// if no method was found, try to find a closure defined as a field of the class and run it
Object value = null;
final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
if (metaProperty != null)
value = metaProperty.getProperty(object);
else {
if (object instanceof Map)
value = ((Map)object).get(methodName);
}
if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
Closure closure = (Closure) value;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
}
if (object instanceof Script) {
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
if (bindingVar != null) {
MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar);
return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
}
}
return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_5_X/src/main/groovy/groovy/lang/MetaClassImpl.java#L1262-L1287
Now, pay attention to the branch if (object instanceof Script) and how the binding variable gets retrieved. It tries to retrieve test variable from binding object using:
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
Your code would work if it was:
Object bindingVar = ((Script) object).getBinding().getVariable(methodName);
instead. But it's not.
You can make your second case working if you override getVariables() method instead of getVariable(String name), for instance:
class MyBinding extends Binding {
#Override
Map getVariables() {
return super.getVariables() + [
test: new Test()
]
}
}
Of course, your final implementation might be much more sophisticated. (E.g. you could get super.getVariables() map first, check which variables are missing and add a default variable only if the initial map was missing given variable.) But this is up to you.
Alternatively, consider using methodMissing instead of the binding variable fallback. It could make your code much easier to read and reason about.
In Kotlin, there is the apply method:
inline fun <T> T.apply(block: T.() -> Unit): T (source)
Calls the specified function block with this value as its receiver and returns this value.
This allows you to configure an object like the following:
val myObject = MyObject().apply {
someProperty = "this value"
myMethod()
}
myObject would be the MyObject after the apply {} call.
Groovy has the with method, which is similar:
public static <T,U> T with(U self,
#DelegatesTo(value=DelegatesTo.Target.class,target="self",strategy=1)
Closure<T> closure
)
Allows the closure to be called for the object reference self.
...
And an example from the doc:
def b = new StringBuilder().with {
append('foo')
append('bar')
return it
}
assert b.toString() == 'foobar'
The part with the Groovy method is always having to use return it to return the delegate of the with call, which makes the code considerably more verbose.
Is there an equivalent to the Kotlin apply in Groovy?
The function is called tap and is part of Groovy 2.5. See discussions about the naming in merge request.
Other than that, only foo.with{ bar=baz; it } can be used. You can retrofit your own doto, tap, apply, ... via metaprogramming.
I'm trying to put into the field an object that supports a call operation, and then to call him. I can do it without intermediate reading fields in a variable?
My attempt looks like this:
class CallableObjectDynamic {
def call() {
return "5"
}
}
class MyClassDynamic {
CallableObjectDynamic field = new CallableObjectDynamic()
}
class GroovyRunnerDynamic {
static String make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field()
}
}
But I receive groovy.lang.MissingMethodException.
What can you do? Or can anyone give a proof where it's written that we can't call the field?
Membership (.) has lower order of precedence than function/method/call invocation (()). Thus this line:
return x.field()
is interpreted as "invoke the 'field' method on the 'x' object".
To get Groovy to parse the code as you desire, the minimal change would be to regroup using parentheses, as follows:
return (x.field)()
which is (ultimately) interpreted as "invoke the 'call' method on the 'field' object member of the 'x' object", as desired.
It is trivial issue. Not required to have parenthesis for field.
Change from:
return x.field()
To:
return x.field
If you want to execute call method further, then use below code snippet.
Note that static method return type is changed.
class CallableObjectDynamic {
def call() {
return "5"
}
}
class MyClassDynamic {
CallableObjectDynamic field = new CallableObjectDynamic()
}
class GroovyRunnerDynamic {
static def make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field
}
}
GroovyRunnerDynamic.make(1).call()
Output would be : 5
Not sure why argument to make method is done here, seems to be not used in the above code.
Alternatively, you can change
class GroovyRunnerDynamic {
static def make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
return x.field.call()
}
}
GroovyRunnerDynamic.make(1)
EDIT: Based on OP's implicit call.
Not really sure how it is working, but the below does implicit call. Just assign x.field to a variable and just add parenthesis for that as shown below.
class GroovyRunnerDynamic {
static String make(int arg1) {
MyClassDynamic x = new MyClassDynamic()
def fun = x.field
fun()
}
}
GroovyRunnerDynamic.make(1)
I have something like the following groovy class :
class Foo {
private Map<String,String> bar = [:]
Map<String, String> getBar() {
return bar.asImmutable()
}
def doSomething(List<String> argValues){
argValues.each {
bar[it] = it
}
}
}
The doSomething method will fail, it seem, in the each closure, the bar property is use trough accessor, not the field. So it's immutable.
The question is "how can i use the field (not the accessor) within the closure ?
Thanks.
You can use the property accessor operator # like so:
this.#bar[ it ] = it
I seem unable to use a Closure as a parameter to a superclass constructor when it is specified inline.
class Base {
def c
Base(c) {
this.c = c
}
void callMyClosure() {
c()
}
}
class Upper extends Base {
Upper() {
super( { println 'called' } )
}
}
u = new Upper()
u.callMyClosure()
The compilation fails with the message Constructor call must be the first statement in a constructor..
I realize this a somewhat strange use-case, and I can design around it for the time being. But I'm interested, is this is to be expected? Or have I got the syntax incorrect?
I think that the problem is related to the fact that Groovy turns the constructor into something different while trying to compile it as a Java class. Maybe the closure definition is expanded before the call to super generating that error.
A workaround is to define the closure outside the constructor itself:
class Base {
def c
Base(c) {this.c = c}
void callMyClosure() {
c()
}
}
class Upper extends Base {
static cc = {println 'called'}
Upper() {
super(cc)
}
}
u = new Upper()
u.callMyClosure()
It's not so nice but at least it works.. another way could be to define the closure by using a normal new Closure(...) syntax
It might be confusing a closure and a block...can you try
super( { -> println 'called' } )