Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a clean way to specify Closure parameter type in Groovy?

I'm aware of @ClosureParams annotation. It seems to be covering more complex use cases only though. I'm looking for something like described here at the annotating closures section. Which is similar to the following snippet:

void doSomething(MyType src, @ClosureParams(MyType) Closure cl) { ... }

This example no longer compiles with more recent groovy versions unfortunately (I'm on 2.5.8 at the moment). I know I can achieve equivalent with:

void doSomething(MyType src, @ClosureParams(FirstParam) Closure cl) { ... }

My use case doesn't have any other argument than closure itself though:

void doSomething(@ClosureParams(/* how? */) Closure cl) { ... }

I can hack it like:

void doSomething(@ClosureParams(SecondParam) Closure cl, MyType ignore = null) { ... }

It's far from clean, is it not?

I can as well go:

void doSomething(@ClosureParams(value = SimpleType, options = ['com.somepackage.MyType']) Closure cl) { ... }

It's not only ugly and noisy but as well having type specified as string prevents some IDE features from working. For example MyType refactor-rename or search for usages won't be picked up here.

I guess, there isn't any cleaner way of achieving this so type could be specified as a type not a string and without an extra unnecessary argument, is there?

Something like originally posted by Cédric Champeau in the blog post linked above would be ideal. Which in my case would look like:

void doSomething(@ClosureParams(MyType) Closure cl) { ... }
like image 844
topr Avatar asked Nov 07 '22 12:11

topr


1 Answers

You may want to consider FromAbstractTypeMethods signature hint instead of SimpleType. It is quite verbose to use, but it gives you benefits that are missing from SimpleType hint class - you can easily refactor types defined in the signatures class, as well as you can find usages of classes used in the signature hint. The main downside is that you need to create additional abstract class per closure signature hints, and the name of the class that contains signatures as abstract methods need to be defined as a constant string (the same problem exists with the SimpleType signature hint.) However, you get a single parameter doSomething method, without adding the second null parameter just to be able to use SecondParam signature hint.

package com.example

import groovy.transform.Immutable
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FromAbstractTypeMethods

class MyClass {
    static void doSomething(@ClosureParams(value = FromAbstractTypeMethods, options = ["com.example.MySignatures"]) Closure cl) {
        cl.call()
    }

    static void main(String[] args) {
        doSomething {
            println it.name
        }
    }
}

@Immutable
class MyType {
    String name
    int x
    int y
}

abstract class MySignatures {
    abstract void firstSignature(MyType myType)
    abstract void secondSignature(MyType myType, String str)
}

enter image description here

I guess the simple and clean @ClosureParams(String) variant was removed to satisfy other more complex use cases. The API of ClosureParams annotation is fixed and it limits options to array of strings. Maybe it could be achieved by implementing own ClosureSignatureHint - I have tried that several months ago, but I couldn't make IntelliJ IDEA to use my custom class to provide signature hints.

like image 146
Szymon Stepniak Avatar answered Nov 15 '22 05:11

Szymon Stepniak