IgorekPotworek
IgorekPotworek

Reputation: 1345

Custom implicit coercion in groovy

Is there in groovy similar feature to scala's implicit conversions?

I now that we can define custom type coercion like here: link, but there is need for 'as' keyword to add to use it.

To provide some concrete use case: I want to create conversion from String to Currency and after enabling it I want to pass String to method which requires Currency as a param.

public class SomeJavaClass { 
    void method(Currency currency){
    ...
    }
}

and after implicit conversion this code should be valid:

class SomeGroovyClass {
    def method(){
        new SomeJavaClass().method("USD")
    }
}

Is this somehow possible?

Upvotes: 0

Views: 275

Answers (1)

jihor
jihor

Reputation: 2599

Groovy offers no implicit coercion, but dynamic features of the language allow to override methodMissing() on the callee class (depending on whether the callee method is static, methodMissing() or $static_methodMissing() should be overridden, see docs):

class ShrewdCalculator {
    static void accept(Currency o) {
        println o
    }

    static def $static_methodMissing(String name, def args) {
        if (name == "accept" && args.size() == 1 && args[0].class == String) {
            return accept(Currency.valueOf(args[0]))
        }
        throw new MissingMethodException(name, this.class, args)
    }
}

where Currency is e.g.

enum Currency {
    US_DOLLAR, EURO

    static valueOf(String s) {
        if (s == "USD") return US_DOLLAR
        if (s == "EUR") return EURO
        throw new RuntimeException("No currency defined for input $s")
    }
}

Then the method invocations will yield the following results:

ShrewdCalculator.accept(Currency.US_DOLLAR) // --> US_DOLLAR
ShrewdCalculator.accept("USD") // --> US_DOLLAR thanks to fallback on methodMissing()

Another way is to add a coercion method to the ExpandoMetaClass of the String class and simply use Object in the method signature only to coerce it manually under the hood:

class ShrewdCalculator {
    static {
        String.metaClass.asType(Currency) {
            return Currency.valueOf(this)
        }
    }

    static void accept(Object o) {
        println o as Currency
    }
}

In this case, the method will work like this:

ShrewdCalculator.accept(Currency.US_DOLLAR) // --> US_DOLLAR because Currency as Currency is no-op
ShrewdCalculator.accept("USD") // --> US_DOLLAR thanks to coercion defined in String's metaclass

Neither of these methods is akin to Scala's implicits, though.

Upvotes: 1

Related Questions