How do I convert a Groovy class constructor into a Closure? - groovy

So Groovy has this relatively handy syntax to convert methods into closures, e.g.
[1,2,3].each { println it }
// is equivalent to
[1,2,3].each this.&println
But how do I convert a class Constructor, e.g
[1,2,3].collect { new Thing( it ) }
// is equivalent to
[1,2,3].collect ????
Groovy's reflection has Thing.constructors List to inspect, but I can't figure out where to put the ampersand in Thing.constructors[0].

You can use invokeConstructor metaClass method that invokes a constructor for the given arguments.
class Thing {
Thing(Integer num) { this.num = num }
Integer num
}
[1,2,3].collect Thing.metaClass.&invokeConstructor

Related

Create GString from String

We're using groovy in a type-safe way. At some point I want to invoke a method with signature
void foo(GString baa)
As long the String I enter contains some ${baz} everything is fine, but when I use a pure String I get a compile error
foo("Hello, ${baz}") // fine
foo("Hello, world") // Cannot assign value of type java.lang.String to variable of type groovy.lang.GString
foo("Hello, world${""}") // fine but ugly
Is there a nice way to create a GString out of String?
EDIT
Guess I've oversimplicated my problem. I'm using named constructor parameters to initialize objects. Since some of the Strings are evaluated lazily, I need to store them as GString.
class SomeClass {
GString foo
}
new SomeClass(
foo: "Hello, world" // fails
)
So method-overloading is no option.
The solution is as mentioned by willyjoker to use CharSequence instead of String
class SomeClass {
CharSequence foo
}
new SomeClass(
foo: "Hello, world" // works
)
new SomeClass(
foo: "Hello, ${baa}" // also works lazily
)
There is probably no good reason to have a method accepting only GString as input or output. GString is meant to be used interchangeably as a regular String, but with embedded values which are evaluated lazily.
Consider redefining the method as:
void foo (String baa)
void foo (CharSequence baa) //more flexible
This way the method accepts both String and GString (GString parameter is automagically converted to String as needed). The second version even accepts StringBuffer/Builder/etc.
If you absolutely need to keep the GString signature (because it's a third party API, etc.), consider creating a wrapper method which accepts String and does the conversion internally. Something like this:
void fooWrapper (String baa) {
foo(baa + "${''}")
}
You can create overloaded methods or you can use generics; something as below:
foo("Hello from foo GString ${X}")
foo("Hello from foo")
MyClass.foo("Hello from foo GString ${X}")
MyClass.foo("Hello from foo")
// method with GString as parameter
void foo(GString g) {
println("GString: " + g)
}
// overloading method with String as parameter
void foo(String s) {
println("String: " + s)
}
// using generics
class MyClass<T> {
static void foo(T t) {
println(t.class.name + ": " + t)
}
}

Groovy and Java Callbacks / Lambdas (Closures with a type)

This is a callback I have in Java (future is a Mongo's SingleFutureResult):
future.register(new SingleResultCallback<ArrayList<Document>>() {
#Override
void onResult(ArrayList<Document> documents, MongoException e) {
//do something
}
})
With Java 8 I can use Lambdas to make it look like this:
future.register((documents, e) -> {
//do something
});
Neat. However, I would like to call that Java method within a Groovy class. I know Groovy does currently not support jdk8 lambda's syntax, but the docs always stress that they have closures instead. If I try to use a closure in such a case it fails, as the closure is of type Closure, and not of type SingleResultCallback. Is there a way to have something like a closure with a different type? Is there any other way to make this work in a nice way? I know I can use the first solution, but that looks pretty weird within a groovy class.
Use Groovy's closure syntax:
future.register( { documents, e ->
//do something
});
If there is ambiguity in a register method overload, it might need as SingleResultCallback after the closure declaration:
future.register { documents, e -> } as SingleResultCallback
This is an ambiguity scenario:
interface SingleResultCallback<T> {
def onResult(T result)
}
interface Callback { def onResult(obj) }
class Future {
void register(SingleResultCallback s) {
println "registered: ${s.onResult(10)}"
}
void register (Callback r) {
println "runnable=${r.onResult()}"
}
}
new Future().register { it ** 2 }
Which fails with:
Caught: groovy.lang.GroovyRuntimeException:
Ambiguous method overloading for method Future#register.
Cannot resolve which method to invoke for [class Sam$_run_closure1] due
to overlapping prototypes between:
[interface Callback]
[interface SingleResultCallback]
And if SingleResultCallback had more than one method, a map coercion is a nice way to deal with it:
interface SingleResultCallback<T> {
def onResult(T result)
def onError
}
class Future {
def register(SingleResultCallback s) {
"registered: ${s.onResult(10)}"
}
}
assert new Future().register(
[onResult: { it ** 2 } ] as SingleResultCallback ) == "registered: 100"
I know it has been a while since the question was made, and it's already replied, but i would also to add this example using Groovy Closure as callback (to see more go at https://groovy-lang.org/closures.html).
Groovy also support Closure as an Object, for instance:
Closure callback = { println 'Done!' }
callback()
The output would be:
Done!
Above it's just another example:
def callbackList(){
def list = ["RICE", "BEANS", "EGGS"]
iterableList(list, { elem ->
println elem
})
}
def iterableList(list, callback){
list.each {
callback(it)
}
}
The output would be:
RICE
BEANS
EGGS

Method aliasing in class with Groovy

I'm going to internationalize groovy API abit.
For final class (e.g. String)
String.metaClass.вСтроку = {-> this.toString() }
However, this will create additional closure. Isn't there any way to just alias method with another method?
Something like this:
String.metaClass.вСтроку = String.metaClass.&toString
You could use #Category transform like this
#Category(String) class StringInternationalization {
String вСтроку() {
this.toString()
}
int длина() {
this.length()
}
}
class ApplyMixin {
static {
String.mixin(StringInternationalization)
final helloString = "Привет мир!"
println helloString.вСтроку()
assert helloString.длина() == helloString.length()
}
}
new Main()
This will create 1 Category class for each localised class and one class to apply all mixin transformations(to register all methods.) Also should be faster, then individual closures.
More reading here: http://groovy.codehaus.org/Category+and+Mixin+transformations

Groovy AST Transformations - How can I figure out the return type of a MethodCallExpression?

With Groovy AST Transformations, how can I figure out the return type of a MethodCallExpression?
MethodCallExpression.getType() always returns java.lang.Object even if I explicitly define the return type of the method in the method definition.
Due to the dynamic nature of groovy, the AST can't know the return type of a method call expression at compile time. For example:
class Example {
String foo() { "foo" }
}
def e = new Example()
assert e.foo() == "foo"
Looks simple enough. foo returns a string, so the MethodCallExpression for e.foo() should have a type of String, right? But what if foo is changed in the metaClass?
class Example {
String foo() { "foo" }
}
def e = new Example()
if (someRuntimeCondition) {
e.metaClass.foo = { -> 42 }
}
assert e.foo() == "foo" // is foo a String or an Int?
The groovy compiler just doesn't have enough information to make any assumptions about the method call since it could change at runtime, so it has to compile it down to an Object.

Using a Closure as an argument to a superclass constructor

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' } )

Resources