st-h
st-h

Reputation: 2553

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.

Upvotes: 1

Views: 4881

Answers (2)

N&#237;kolas La Porta
N&#237;kolas La Porta

Reputation: 531

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

Upvotes: 1

Will
Will

Reputation: 14519

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"

Upvotes: 2

Related Questions